正睿 2019 省选十连测 Day1 T2 壕

通过贪心来不断找性质最后决定dp形式的一道思维题

先把数组转化成还需要加多少才能变成全 0(也就是在模 7 意义下取反)由于是
区间加的操作,可以直接转差分数组(此处首位各加上一个 0 然后差分),变成
两个位置上一加一减。

一个非常直观的性质是,如果我们把最优策略下的每个差分数组上的操作,看成
在操作的两个位置之间连一条边,那么连出来的图肯定是一个森林,且每棵树所
有位置的值(注意:是差分数组中的值)的和在模 7 意义下应该为 0(因为每次
操作,差分数组上两个对应位置的和是没有产生变化的)。而对于一个森林来讲,
最后的操作次数是T-1。T是树的大小。

进而我们可以发现,我们所要做的就是把差分数组上面的值分成尽量多的,值的
和在模7意义下为0的组,对每一组都可以用大小减1次操作搞成全0。

显然的贪心是,0全部放一组,1和6一一配对,2和5一一配对,3和4一一配对。
初步贪心之后,剩下的值只会有三种。
令f[x][y][z]表示三种数分别有x,y,z个的时候最多能搞出来多少组。
枚举分组进行转移,复杂度视实现而定,一般来讲在O(n^4)到O(n^6)之间。
出题人想良心一点,所以只要不是暴力转移,应该都能卡着
时间过去。

转移的时候明显不需要枚举分组。
可以用一个新的状态f[x][y][z][k]。
表示已经三种数分别用了x,y,z,个,
当前最后一组的值模7意义下是k的情况下,前面组好的全0的组最多有多少个。
那么转移是O(1)的。时间复杂度可以降到O(n^3)。
但需要滚动数组。标程就是这样实现的。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define N 1100
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
int a,b,c,na,nb,nc,f[N],cnt[N],dp[50][200][500][7];
int dfs(int x,int y,int z,int k)
{
    #define o dp[x][y][z][k]
    if(!x&&!y&&!z)o=0;
    if(o!=-1)return o;
    o=inf;
    if(x)o=min(o,dfs(x-1,y,z,(k+a)%7)+(((k+a)%7)?1:0));
    if(y)o=min(o,dfs(x,y-1,z,(k+b)%7)+(((k+b)%7)?1:0));
    if(z)o=min(o,dfs(x,y,z-1,(k+c)%7)+(((k+c)%7)?1:0));
    return o;
    #undef o
}
int main()
{
    int n=read(),i,ans=0;
    for(i=1;i<=n;i++)f[i]=read(),cnt[(f[i]-f[i-1]+7)%7]++;
    if(cnt[1]>=cnt[6])a=1;else a=6;na=abs(cnt[1]-cnt[6]);ans+=min(cnt[1],cnt[6]);
    if(cnt[2]>=cnt[5])b=2;else b=5;nb=abs(cnt[2]-cnt[5]);ans+=min(cnt[2],cnt[5]);
    if(cnt[3]>=cnt[4])c=3;else c=4;nc=abs(cnt[3]-cnt[4]);ans+=min(cnt[3],cnt[4]);
    if(nc<na)
    {
        swap(c,a);
        swap(nc,na);
    }
    if(nc<nb)
    {
        swap(c,b);
        swap(nc,nb);
    }
    if(nb<na)
    {
        swap(b,a);
        swap(nb,na);
    }
    memset(dp,-1,sizeof(dp));
    ans+=dfs(na,nb,nc,0);
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Creed-qwq/p/10056420.html