POJ 1185 HDU 4539 状态压缩DP

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

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 10000007
typedef 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 10000007
typedef 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;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值