Shuffle UVA - 12174 解题报告

版权声明:大鹏专属 https://blog.csdn.net/NCC__dapeng/article/details/88283553

疯狂刷题!

题目大意:给你一个长度为N的播放记录,让你求一求随机排序所发生的时间有多少中可能性。

思路:连续的S个数,首先会让我们想到滑动窗口,但是只是简单的使用滑动窗口并不能解决此题,一开始的思路是,使用滑动窗口,然后记录窗口中每个数出现的次数,如果每个数书都唯一,那么便是符合的,即前面的可以随意划分,但是这种利用贪心思想的滑动窗口是并不成立的,它会导致很多情况的漏判,比如第一个窗口虽然符合,但是会影响到后面的其他窗口,此时我们判断的结论是不可以,但是如果抛弃第一个窗口去匹配后面的,这道题是可以有解的。然后去翻了翻博客,看到一个大佬犯了同样的问题,然后在那里学到了

#include <bits/stdc++.h>
using namespace std;

const int INF=0x3f3f3f3f;
const int maxn=100000+10;
int s,n,T;
int p[maxn],c[maxn];//C就相当于滑动窗口的记录数组
bool flag[maxn];

void init()
{
    memset(flag, 0, sizeof(flag));//注意一定要清空还原
    memset(c, 0, sizeof(c));
    
    int re=0;
    scanf("%d %d",&s,&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&p[i]);
        if(i<s)
        {
            if(c[p[i]]) re++;
            c[p[i]]++;
        }
    }

    for(int i=0;i<n;i++)
    {
        if(re==0) flag[i]=true;

        if(c[p[i]]==2) re--;
        c[p[i]]--;

        int k=i+s;//代表窗口滑动
        if(k>=n) continue;
        if(c[p[k]]) re++;
        c[p[k]]++;
    }
}

bool judge(int x)
{
    for(int i=x;i<n;i+=s) if(!flag[i]) return false;
    return true;
}

int solve()
{
    int ans=0;
    memset(c,0,sizeof(c));//注意C数组要及时还原
    for(int i=0;i<s;i++)
    {
        if(judge(i)) ans++;
        if(i>=n) continue;
        if(c[p[i]]) break;//如果前S个中有重复,则必须进行划分。
        c[p[i]]++;
    }
    return ans;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        printf("%d\n",solve());
    }
    return 0;
}

正确的解题方法,滑动窗口加预处理加技巧性暴力枚举。

首先用滑动窗口在o(n) 时间内求出 以i为开头的连续s个的序列中是否满足都是[唯一]的,这句话稍微解释一下,意思就是判断每一次窗口的移动,所产生的新的窗口是否符合要求,这个就满足了贪心的不足,只顾前不顾后。

然后枚举前s个开头,如果 i+=s ,i<=n的所有区间都满足[唯一]性,则当前位置可以划分,因为我们在第一步中已经准确判断了各个窗口是否符合,为什么只枚举前S个也很简单,因为如果前S个都已经不符合了,那后面的在符合也没办法去划分,所以要想划分成功,前S个里至少有一个是可以划分成功的。

其中的技巧性暴力十分的关键,希望可以好好的理解,下面给出AC代码:

猜你喜欢

转载自blog.csdn.net/NCC__dapeng/article/details/88283553