1. 程式人生 > >正睿 2019 省選十連測 Day1 T2 壕

正睿 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;
}