状压dp POJ3254+POJ1185

位运算补充

& 与 同为1为1 or 为0

| 或 同0为0 or 为1

^ 异或 不同为1 相同为0

~ 取反 去相反的

左移 << 右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2。

右移 同理

POJ 3254

题目链接:http://poj.org/problem?id=3254

题目大意: 农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的(用1标记),农夫可以在这些格子里放牛,其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(注意:任何格子都不放也是一种选择,不要忘记考虑!

       转移方程: dp[i][j]+=dp[i-1][k](第一维是行,第二行是当前状态)

       & 两个1就出现1 ,否则 是 0;

       判断是否可以在地图上放下   if((map[x]&y)!=y)   

       判断出现相邻的都放的情况   if( (y & (y<<1))!=0)

       判断i-1行状态j 和i行状态 k是否可以放置  if((j & k)!=0) continue;

#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
const int mod=100000000;
const int maxn=13;
int map[maxn];
int dp[maxn][1<<maxn];

bool judge(int x,int y)
{
    if((map[x]&y)!=y) return 0; //判断土地能否放下
    if( (y & (y<<1))!=0) return 0; //判断是否有相邻的情况
    return 1;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    memset(map,0,sizeof(map));

    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int k;
            scanf("%d",&k);
            map[i]=(map[i]<<1)+k;
        }
    }
    dp[0][0]=1;

    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<=(1<<m);++j)
        {
            if(judge(i,j)==0) continue;
            for(int k=0;k<=(1<<m);++k)
            {
                if((j & k)!=0) continue;
                dp[i][j]+=dp[i-1][k];
                dp[i][j]%=mod;
            }
        }
    }

    int ans=0;
    for(int k=0;k<=(1<<m);++k)
        ans=(ans+dp[n][k])%mod;
    printf("%d\n",ans);
    return 0;
}

POJ 1185 http://poj.org/problem?id=1185

中文题目。题意略

预处理:

     循环1<<m  如果满足左右两格子都没有炮的话把这个状态存起来,同时数出有多少个1

做法:   

    dp[i][j][k] 表示i行 j 表示 i行的状态 k表示 i-1行的状态 由dp[i-1][k][l]转移过来 加上 j状态有多少个1 即可

#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
using namespace std;
int map[110];
int dp[110][210][210];
int num[210][2];
int cnt=0;
bool judge(int x,int y)
{
   if((map[x]&y)!=y) return 0;
   return 1;
}
int main()
{

    int n,m;
    scanf("%d%d",&n,&m);
    memset(map,0,sizeof(map));
    memset(dp,0,sizeof(dp));
    memset(num,0,sizeof(num));
    for(int i=0;i<=(1<<m);++i)
    {
        if((i&(i<<1))||(i&(i<<2))) continue;
        num[cnt][1]=i;
        int k=i;
        while(k)
        {
            if(k%2==1) num[cnt][2]++;
            k/=2;
        }
        cnt++;
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            char  c; int k;
            scanf(" %c",&c);
            if(c=='P') k=1; else k=0;
            map[i]=(map[i]<<1)+k;
        }
    }
    int ans=0;
    for(int j=0;j<cnt;++j)
    {
         if(judge(1,num[j][1])==0) continue;
         dp[1][j][0]+=num[j][2];
         ans=max(ans,dp[1][j][0]);
    }


    for(int i=2;i<=n;++i)
    {
        for(int j=0;j<cnt;++j)
        {
            if(!judge(i,num[j][1])) continue;
            for(int k=0;k<cnt;++k)
            {
                if(!judge(i-1,num[k][1])) continue;
                if((num[j][1] & num[k][1])) continue;
                for(int l=0;l<cnt;++l)
                {
                    if(!judge(i-2,num[l][1])) continue;
                    if((num[k][1] & num[l][1])) continue;
                    if((num[j][1] & num[l][1])) continue;
                    dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[j][2]);
                    ans=max(ans,dp[i][j][k]);
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}


  

    

       


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值