POJ 1185 HDU 4539 状态压缩DP

来源:互联网 发布:广州手机数据恢复 编辑:程序博客网 时间:2024/06/06 01:37

这两个题非常相似  画图出来的效果是;

POJ 1185


HDU  4539



dp[i][j][k] 表示第i行状态为j 第i-1行状态为k   

转移方程为: dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+cnt[j])

确定本行放置是否合法 :

  图1   X&(X<<1)  X&(X<<2) 这2种情况为1了说明在这个炮兵距离为1 2处有另外的炮兵 状态不合法

  图2  只需判断 X&(x<<2)是否为1


判断3行的兼容性:

图1   因为放置的炮兵都在同一列  所以 只用判断2个合法状态相与是否为1就可以了 

图2    当前行 i 与i-1行  兼容的条件是   当前行 左移或右移 再与其相与是否合法  第i和i-2行 直接判断相与即可  如图所示


POJ 1185代码:

#include<stdio.h>#include<math.h>#include<string.h>#include<algorithm>#include<stdlib.h>#include<math.h>using namespace std;#define maxn 110#define inf 10000007typedef long long ll;int n,m;char str[110];  //存初始图int dp[110][70][70];  //dp[[i][j][k]int statu[110];  //初始每行的状态int legal[110];  //合法状态int cnt[70]; //每个合法状态中二进制中1的个数int num; //多少个合法状态,枚举出来void init()  //初始化,读入图{    int i,j;    memset(statu,0,sizeof(statu));    for(i=0;i<n;i++)    {        scanf("%s",str);        for(j=0;j<m;j++)        {            if(str[j]=='H')            statu[i]=(statu[i]<<1)|1;            else            statu[i]=statu[i]<<1;        }    }} int count_one(int x)  //求X二进制中1的个数 {    int count=0;    while(x)    {        count++;        x=x&(x-1);    }    return count;}bool ok(int x)  //判断状态是否合法{    if(x&(x<<1)) return 0;    if(x&(x<<2)) return 0;    return 1;}void count_stu() //枚举出一行中所有合法的摆放状态{    num=0;    int total=1<<m;    for(int i=0;i<total;i++)    {        if(ok(i))        {            legal[num]=i;            cnt[num++]=count_one(i);        }    }}void Dp()  //dp转移,求出最终结果{    int i,j,k,l;    memset(dp,-1,sizeof(dp));    for(i=0;i<num;i++)   //初始化DP数组     {        if(statu[0]&legal[i]) continue; //枚举每个合法状态与初始状态是否冲突        dp[0][i][0]=cnt[i];    }    for(i=1;i<n;i++){        for(j=0;j<num;j++){            if(legal[j]&statu[i]) continue; //枚举当前行每个合法状态与初始状态是否冲突            for(k=0;k<num;k++){                if(legal[j]&legal[k]) continue; //当前行与上一行是否冲突                for(l=0;l<num;l++){                    if(legal[j]&legal[l]) continue; //当前行与上上一行是否冲突                    if(dp[i-1][k][l]==-1) continue;                    dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+cnt[j]);                }            }        }    }}void print()  //打印输出,找出到达状态n-1时最大的值{    int i,j;    int ans=0;    for(i=0;i<num;i++)     for(j=0;j<num;j++)       ans=max(ans,dp[n-1][i][j]);    printf("%d\n",ans);}int main(){    freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&m)==2)    {        init();        count_stu();        Dp();        print();    }    return 0;}

HDU 4539 代码

#include<stdio.h>#include<math.h>#include<string.h>#include<algorithm>#include<stdlib.h>#include<math.h>using namespace std;#define maxn 110#define inf 10000007typedef long long ll;int n,m;//char str[110];int dp[110][200][200];int statu[110];int legal[200];int cnt[200];int num;void init(){    int i,j;    memset(statu,0,sizeof(statu));    int tmp;    for(i=0;i<n;i++)    {        for(j=0;j<m;j++)        {            scanf("%d",&tmp);            if(tmp==0)            statu[i]=(statu[i]<<1)|1;            else            statu[i]=statu[i]<<1;        }    }}int count_one(int x){    int count=0;    while(x)    {        count++;        x=x&(x-1);    }    return count;}bool ok(int x){   // if(x&(x<<1)) return 0;    if(x&(x<<2)) return 0;  //这里变化了    return 1;}void count_stu(){    num=0;    int total=1<<m;    for(int i=0;i<total;i++)    {        if(ok(i))        {            legal[num]=i;            cnt[num++]=count_one(i);        }    }   // printf("%d\n",num);}void Dp(){    int i,j,k,l;    memset(dp,-1,sizeof(dp));    for(i=0;i<num;i++)    {        if(statu[0]&legal[i]) continue;        dp[0][i][0]=cnt[i];    }    for(i=1;i<n;i++){        for(j=0;j<num;j++){            if(legal[j]&statu[i]) continue;            for(k=0;k<num;k++){                if((legal[j]<<1)&legal[k]) continue; //当前行左移与上一行                if((legal[j]>>1)&legal[k]) continue;  //当前行右移与上一行                for(l=0;l<num;l++){                    if((legal[k]<<1)&legal[l]) continue; //上一行左移与上上一行                    if((legal[k]>>1)&legal[l]) continue; //上一行右移与上上一行                    if(legal[j]&legal[l]) continue;  //当前行与上上一行                    if(dp[i-1][k][l]==-1) continue;                    dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+cnt[j]);                }            }        }    }}void print(){    int i,j;    int ans=0;    for(i=0;i<num;i++)     for(j=0;j<num;j++)       ans=max(ans,dp[n-1][i][j]);    printf("%d\n",ans);}int main(){    freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&m)==2)    {        init();        count_stu();        Dp();        print();    }    return 0;}


原创粉丝点击