状态压缩dp总结 长期更新




状压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
#include
#include
#include
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



西电oj 1038


思路:

这个题目和炮兵阵地那个题目很像很像,不过这个是要割出来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
#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





你可能感兴趣的:(状态压缩dp)