POJ 3254 Corn Fields(状压DP入门)

本文介绍了一种解决特定矩阵中放牛方案计数问题的动态规划算法。在一个m*n的矩阵里,部分格子可以放牧,部分不可以,要求相邻格子不能同时放牛。文章通过详细的代码示例讲解了如何计算所有合法放牛方案的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:一个m*n(m, n <= 12)矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,

求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)


思路:可以看代码中的注释。转移方程:dp[i][sta] = ∑(dp[i-1][sta']) (sta‘为与sta不冲突的状态)


带注释代码:

#include <cstdio>
#include <cstring>
using namespace std;

#define mod 100000000
int M,N,top = 0;
//top表示每行最多的状态数

int state[600],num[110];  
//state存放每行所有的可行状态(即没有相邻的状态
//

int dp[20][600];
//dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
int cur[20];
//cur[i]表示的是第i行整行的情况

inline bool ok(int x){	//判断状态x是否可行
   if(x&x<<1)	return false;//若存在相邻两个格子都为1,则该状态不可行
   return true;
}
void init(){			//遍历所有可能的状态
   top = 0;
   int total = 1 << N; //遍历状态的上界
   for(int i = 0; i < total; ++i){
       if(ok(i))state[++top] = i;	
   }
}
inline bool fit(int x,int k){ //判断状态x 与第k行的实际状态的逆是否有‘重合’
   if(x&cur[k])return false; //若有重合,(即x不符合要求)
   return true;  //若没有,则可行
}

int main(){
    while(scanf("%d%d",&M,&N)!= EOF){
       init();
       memset(dp,0,sizeof(dp));
       for(int i = 1; i <= M; ++i){
           cur[i] = 0;
           int num;
           for(int j = 1; j <= N; ++j){  //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
                scanf("%d",&num);  //表示第i行第j列的情况(0或1)
               if(num == 0) //若该格为0	
				   cur[i] +=(1<<(j-1)); //则将该位置为1(注意要以相反方式存储,即1表示不可放牧
           }
       }
       for(int i = 1;i <= top;i++){
           if(fit(state[i],1)){  //判断所有可能状态与第一行的实际状态的逆是否有重合
                dp[1][i] = 1;  //若第1行的状态与第i种可行状态吻合,则dp[1][i]记为1
           }
       
	   }

	   /*
	   状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
		*/
       for(int i = 2; i <= M; ++i){  //i索引第2行到第M行
           for(int k = 1; k <= top; ++k){ //该循环针对所有可能的状态,找出一组与第i行相符的state[k]
                if(!fit(state[k],i))continue; //判断是否符合第i行实际情况
                for(int j = 1; j <= top ;++j){ //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j]
                   if(!fit(state[j],i-1))continue;  //判断是否符合第i-1行实际情况
                   if(state[k]&state[j])continue;  //判断是否与第i行冲突
                   dp[i][k] = (dp[i][k] +dp[i-1][j])%mod;  //若以上皆可通过,则将'j'累加到‘k'上
                }
           }
       }
       int ans = 0;
       for(int i = 1; i <= top; ++i){ //累加最后一行所有可能状态的值,即得最终结果!!!泥马写注释累死我了终于写完了!
           ans = (ans + dp[M][i])%mod;
       }
       printf("%d\n",ans);
   }
}

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 13;
const int mod = 1e8;
int state[1<<maxn], dp[maxn][1<<maxn];
int cur[maxn], m, n, top;

bool ok(int x)
{
    return !(x&x<<1);
}

void init()
{
    top = 0;
    int total = 1<<n;
    for(int i = 0; i < total; i++)
        if(ok(i))
            state[top++] = i;
}

bool fit(int x, int k)
{
    return !(x&cur[k]);
}

int main(void)
{
    while(cin >> m >> n)
    {
        init();
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= m; i++)
        {
            cur[i] = 0;
            int x;
            for(int j = 1; j <= n; j++)
            {
                scanf("%d", &x);
                if(!x)
                    cur[i] += 1<<(j-1);
            }
        }
        for(int i = 0; i < top; i++)
            if(fit(state[i], 1))
                dp[1][i] = 1;
        for(int i = 2; i <= m; i++)
            for(int k = 0; k < top; k++)
            {
                if(!fit(state[k], i)) continue;
                for(int j = 0; j < top; j++)
                {
                    if(!fit(state[j], i-1)) continue;
                    if(state[k]&state[j]) continue;
                    dp[i][k] = (dp[i][k]+dp[i-1][j])%mod;
                }
            }
        int ans = 0;
        for(int i = 0; i < top; i++)
            ans = (ans+dp[m][i])%mod;
        printf("%d\n", ans);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值