bzoj 1187: [HNOI2007]神奇游樂園 插頭dp


       一道最裸的插頭dp,然而還是調了很久。。代碼2.6k+調試語句1.5k。。不過這次代碼很多可以不需要的但是這種題目關鍵還是要思路清晰。。。

       然而窩用了3進制作死時間被爆出翔。。。。

       令dp[i][j][k]表示在第i行第j列狀態為k時的最大收入,其中k表示插頭的狀態,即一個括號序列。令上面一段連續的在輪廓線上的左端點為'(',右端點為')',或者也可以看成是從左端點出發在輪廓線上面走過某一段連續的路徑之后再右端點回到了輪廓線。那么令'('=1,')'=2,沒有插頭為0,就可以用一個三進制來壓縮狀態了。

       那么可以用除法和取模快速得到某一位,在輪廓線的轉折處有3*3=9中情況分類討論。

       1.左邊和上面都沒有插頭,那么這一格可以選擇不放,或者向下放一個'(',同時向右放一個')';

       2.左邊或者上面有一個'('插頭,那么這一格只能向下放一個'('或者向右放一個'(';

       3.左邊或者上面有一個')'插頭,同上;

       4.左邊和上面都有'('插頭,這種情況比較復雜,顯然是需要把兩個插頭連起來的,但是狀態會有變化,如圖(有點丑QAQ):


       可以發現此時上面的'('插頭對應的')'插頭變成了'('插頭。。。於是為了O(1)轉移我們需要預處理每一個狀態的每一位和它對應的括號的位置。然后修改一下那個位置的狀態即可。

       5.上面和左邊都是')'插頭,同上;

       6.左邊是'('插頭,上面是')'插頭,此時應該判斷其它地方有沒有插頭,如果沒有插頭就更新答案。不進行轉移;

       7.左邊是')'插頭,上面是'('插頭,那么直接連上即可。

       然后就是考驗細心程度的時候了。。。

AC代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1000000000
using namespace std;

int m,n,a[105][7],lp[2187][7],rp[2187][7],f[105][7][2187],c[15]; bool bo[2187];
bool check(int t){
int i,x,s=0;
for (i=0; i<=n; i++){
x=t/c[i]%3;
if (x==1) s++; else if (x==2) s--;
if (s<0) return 0;
}
return !s;
}
int calc(int t){
int i,x,ans=0,s=0;
for (i=0; i<n; i++){
x=t/c[i]%3;
if (x==1){ s++; ans+=a[1][i+1]; }
else if (x==2){ s--; ans+=a[1][i+1]; }
else if (s) ans+=a[1][i+1];
if (s>1) return -inf;
}
return ans;
}
int findl(int t,int p){
int i,x,s=1;
for (i=p-1; i>=0; i--){
x=t/c[i]%3;
if (x==1) s--; else if (x==2) s++;
if (!s) return i;
}
return 0;
}
int findr(int t,int p){
int i,x,s=1;
for (i=p+1; i<=n; i++){
x=t/c[i]%3;
if (x==1) s++; else if (x==2) s--;
if (!s) return i;
}
return n;
}
void up(int &x,int y){ if (x<y) x=y; }
int main(){
scanf("%d%d",&m,&n); int i,j,k,x,u,v,tmp;
for (i=1; i<=m; i++)
for (j=1; j<=n; j++) scanf("%d",&a[i][j]);
c[0]=1; for (i=1; i<=n+1; i++) c[i]=3*c[i-1];
memset(f,192,sizeof(f));
for (i=0; i<c[n+1]; i++) if (check(i)){
bo[i]=1;
for (j=0; j<=n; j++){
k=i/c[j]%3;
if (k==2) lp[i][j]=findl(i,j); else
if (k==1) rp[i][j]=findr(i,j);
}
if (!(i/c[n])) f[1][n][i]=calc(i);
}
int ans=-inf;
for (i=2; i<=m; i++){
for (j=0; j<c[n+1]; j++) if (bo[j] && !(j/c[n])){
u=j%3; x=(j-u)*3; tmp=f[i-1][n][j];
if (!u){
up(f[i][1][x+7],tmp+a[i][1]);
up(f[i][1][x],tmp);
} else if (u==1){
up(f[i][1][x+1],tmp+a[i][1]);
up(f[i][1][x+3],tmp+a[i][1]);
}
}
for (j=2; j<=n; j++)
for (k=0; k<c[n+1]; k++) if (bo[k]){
u=k/c[j-1]%3; v=k/c[j]%3;
x=k-u*c[j-1]-v*c[j]; tmp=f[i][j-1][k];
if (!u && !v){
up(f[i][j][x],tmp);
up(f[i][j][x+c[j-1]+2*c[j]],tmp+a[i][j]);
} else if (!u && v==1 || u==1 && !v){
up(f[i][j][x+c[j-1]],tmp+a[i][j]);
up(f[i][j][x+c[j]],tmp+a[i][j]);
} else if (!u && v==2 || u==2 && !v){
up(f[i][j][x+2*c[j-1]],tmp+a[i][j]);
up(f[i][j][x+2*c[j]],tmp+a[i][j]);
} else if (u==1 && v==1){
up(f[i][j][x-c[rp[k][j]]],tmp+a[i][j]);
} else if (u==2 && v==2){
up(f[i][j][x+c[lp[k][j-1]]],tmp+a[i][j]);
} else if (u==1 && v==2){
if (!x) up(ans,tmp+a[i][j]);
} else{
up(f[i][j][x],tmp+a[i][j]);
}
}
}
printf("%d\n",ans);
return 0;
}


by lych

2016.4.14


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com