狀態壓縮dp總結 長期更新
狀壓dp本人做的題目真的不太多...至今還未理解到其中的精髓.所以以下的思路描述中有存在不當的地方希望能夠指出.另外,有些地方說的比較複雜,因為本弱雞
對這些東西不是很理解.....多寫點有助於理解吧.
思路:
首先,我們可以發現對於每一行的當前位置能不能放炮兵,只與他的上一行和上上一行
的炮兵位置有關係,所以要開一個三維陣列轉移關係.
0表示不放大炮,1表示放大炮,同樣的,先要滿足硬體條件,即有的地方不能放大炮,
然後就是每一行中不能有兩個1的距離小於2(保證橫著不互相攻擊),這些要預先處理一下。然後就是
狀態表示和轉移的問題了,因為是和前兩行的狀態有關,所以要開個三維的陣列來表示狀態,當前行
的狀態可由前兩行的狀態轉移而來。即如果當前行的狀態符合前兩行的約束條件(不和前兩行的大炮
互相攻擊),則當前行的最大值就是上一個狀態的值加上當前狀態中1的個數(當前行放大炮的個數)
【狀態表示】dp[i][j][k] 表示第i行為第j個狀態,第i-1為第k個狀態時的最大炮兵個數。
【狀態轉移方程】 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);//num[j]為第j個狀態中的二進位制1的個數
【DP邊界條件】dp[1][i][0] =num[i] 狀態i能夠滿足第一行的硬體條件
(注意:這裡的i指的是第i個狀態,不是一個二進位制數,開一個數組儲存二進位制狀態)
思考:
對於這種有地形限制的,標記一個點其他周圍都有影響的,一般都要預處理.將所有可能的狀態和地形的限制預處理出來,每次列舉第i個狀態的時候,要首先滿足地形的限制,在滿足各行各列之間的關係.
#include<iostream> #include<cstdio> #include<string.h> #include<algorithm> using namespace std; const int maxn=111; int dp[maxn][maxn][maxn];//dp[i][j][k] 第i行為第j個狀態,第i-1行為第k個狀態時的最大炮兵數 int status[maxn],num[maxn]; //status[i] 當前第i個狀態的二進位制 kk(1放大炮 0 不放). //num[i]存放與status相對應的當前的第i個狀態的二進位制中有多少個1,即放了多少炮兵 char s[maxn]; int mp[maxn];//存放地形的限制,1表示不能放大炮,0表示可以放大炮. int n,m; int _count(int x) { int __=0; while(x) { if(x&1) __++; x>>=1; } return __; } int main() { while(~scanf("%d %d",&n,&m)) { memset(dp,-1,sizeof(dp)); memset(status,0,sizeof(status)); memset(num,0,sizeof(num)); memset(mp,0,sizeof(mp)); for(int i=1;i<=n;i++) { scanf("%s",s); for(int j=0;j<m;j++) { if(s[j]=='H') mp[i]+=(1<<j); } } int cnt=0; for(int i=0;i<(1<<m);i++) { if(!(i&(i<<2))&&!(i&(i<<1)))//一行內的炮兵不能相互攻擊. { num[cnt]=_count(i); status[cnt++]=i; } } for(int i=0;i<cnt;i++) { if(!(status[i]&mp[1])) dp[1][i][0]=num[i];//第一行賦初值. } for(int i=2;i<=n;i++) { for(int j=0;j<cnt;j++)//第i行的第j個狀態 { if(status[j]&mp[i]) //第i行與第i行的地形不衝突 continue; for(int k=0;k<cnt;k++)//第i-1行的第k個狀態 { if(status[k]&mp[i-1]) //狀態與地形不衝突 continue; if(status[j]&status[k]) //第i行與第i-1行不互相攻擊 continue; for(int t=0;t<cnt;t++)//第i-2行的第t個狀態 { if(status[t]&mp[i-2]) //狀態與地形不衝突 continue; if(status[t]&status[k]) //第i-1行與i-2不互相攻擊 continue; if(status[t]&status[j]) //第i行與第i-1行不互相攻擊. continue; dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]); } } } } int ans=0; for(int i=0;i<cnt;i++) for(int j=0;j<cnt;j++) { ans=max(ans,dp[n][i][j]); } printf("%d\n",ans); } return 0; }
思路:
這個題目和炮兵陣地那個題目很像很像,不過這個是要割出來2*2的.還是老規矩,我們先來確定一下需要幾維.由於是2*2,那麼每次割玻璃只會對下面一行有影響.也就是說當前行能否割,只與上一行的狀態有關了,所以二維就足夠了。
其次因為這是一個2*2的我們列舉1表示切方格的時候只能列舉四個中的一小塊,所以這裡我設當第i行第j位為1的時候,那麼他就按照這樣切,表示它和第i+1行的 j-1 j 四塊,構成一個2*2.
01
00
那麼我們最後只需要統計第n-1行的所有可能狀態中二進位制數最多的那個.另外我們發現它只對三個方向和自身地形,4個位置不能衝突.另外需要注意的就是按照我們這種的
切割方式它最前面永遠不可能是1只能是0.所以我們列舉狀態的時候只要列舉到(1<<(m-1))就可以了.
根據炮兵陣地的總結,存在這種地形限制問題的, 我們都需要進行預處理,即把所有可能的情況預處理出來,把自己地形的限制預處理出來,(能切割的地方為0,不能切割的地方為1).
最後給出狀態轉移方程:
dp[i][j]=max(dp[i][j],dp[i-1][k]+num[j])
//dp[i][j]表示第i行的第j個狀態.(是所有可行狀態中的第j個,並不代表一個二進位制數)
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1024;
int dp[maxn][maxn];
int status[maxn],num[maxn];
int mp[maxn];
int t,n,m;
int __(int x)
{
int _=0;
while(x)
{
if(x&1)
_++;
x>>=1;
}
return _;
}
int check_row(int r,int i)//判斷第r行的第i個狀態和r的地形是否衝突
{
int sta=status[i];
if(mp[r]&sta||mp[r+1]&sta||mp[r]&(sta<<1)||mp[r+1]&(sta<<1)) return 0;
return 1;
}
int check_sta(int i,int j)//判斷第i個狀態和第j個狀態是否衝突。
{
int s1=status[i],s2=status[j];
if(s1&s2||s1&(s2<<1)||(s1<<1)&s2) return 0;
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
memset(mp,0,sizeof(mp));
memset(status,0,sizeof(status));
memset(num,0,sizeof(num));
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;
for(int j=0;j<m;j++)
{
scanf("%d",&x);
if(!x)
mp[i]+=(1<<j);
}
}
int cnt=0;
for(int i=0;i<(1<<(m-1));i++)
{
if(!(i&(i<<1)))
{
num[cnt]=__(i);
status[cnt++]=i;
}
}
for(int i=0;i<cnt;i++)
{
if(check_row(1,i))
dp[1][i]=num[i];
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<cnt;j++)//列舉第i行的狀態
{
if(!check_row(i,j))//判斷列舉的當前行地形和第j個狀態是否衝突
continue;
for(int k=0;k<cnt;k++)//列舉第i-1行的狀態.
{
if(!check_row(i-1,k))//判斷第i-1行的地形和當前k狀態是否衝突.
continue;
if(!check_sta(j,k))//判斷上一行的狀態k和當前行狀態j是否衝突
continue;
dp[i][j]=max(dp[i][j],dp[i-1][k]+num[j]);
}
}
}
int ans=0;
for(int i=0;i<cnt;i++)
ans=max(ans,dp[n-1][i]);
printf("%d\n",ans);
}
return 0;
}