状压dp本人做的题目真的不太多...至今还未理解到其中的精髓.所以以下的思路描述中有存在不当的地方希望能够指出.另外,有些地方说的比较复杂,因为本弱鸡
对这些东西不是很理解.....多写点有助于理解吧.
POJ 1185 经典状压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; }