【POJ3254】Corn Fields(状态压缩DP)

这篇博客通过解析POJ3254问题介绍了状态压缩动态规划(DP)的基本概念。作者指出,状态压缩是用一个十进制数表示所有可能的状态,特别是适合于二维格子问题。文章详细分析了题目要求,即在不允许羊相邻的条件下,计算可以放置羊的格子组合数。通过举例说明了如何计算不同状态下的方案数,并强调了需要考虑相邻行的状态以避免冲突。

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

记录一个菜逼的成长。。

一道状态压缩DP入门的题。学习下状压DP。
题目大意:
给你一个n*m的图。为0不能放羊,为1可以放羊,一个格子只能放一只羊,并且不能有羊相邻的情况有多少种。

状态压缩就是将状态用一个十进制数来表示,十进制数用二进制表示就可以表示状态,所以会用到位运算。
dp[i][j]:=第i行状态为j的方案数;
对于可以放羊的位置,如果放羊则为1,不放则为0;
样例第一行有8种情况,分别为 000,001,010,100,011(不符),101,110(不符),111(不符)
第二行有两种情况,分别为000,010
而010这种情况又跟第一行的010冲突,所以对于000情况又5种,对于010有4种,加起来是9种。
所以判断的时候不仅要判断同行相邻,还要判断与上一行相邻的情况。
对于样例,m = 3,所以将状态压缩为8.即1<<3。在二进制下有三位,可以用来表示状态。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <list>
#include <stack>
#include <deque>
#include <cctype>
#include <bitset>
#include <cmath>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a) memset(a,0,sizeof(a))
#define fin freopen("D://in.txt","r",stdin)
#define fout freopen("D://out.txt","w",stdout)
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
typedef vector<PII> VPII;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int mod = 100000000;
const int maxn = (1<<12) + 10;
int dp[15][maxn];
int g[15][15],two[15];
vector<int>a[15];
int n,m;
//预处理2的幂次
void init()
{
    two[0] = 1;
    for( int i = 1; i < 15; i++ )
        two[i] = two[i-1] << 1;
}
int fun(int x)
{
    int ret = 0;
    for( int i = 1; i <= m; i++ ){
        ret += (!g[x][i]) * two[m-i];//注意这里是g[x][i]为0 ,与下面的判断放羊是否合法对应
    }
    return ret;
}
int main()
{
    init();
    while(~scanf("%d%d",&n,&m)){
        cl(a);cl(dp);
        for( int i = 1; i <= n; i++ ){
            for( int j = 1; j <= m; j++ ){
                scanf("%d",g[i]+j);
            }
        }
        int k = 1 << m;
        //处理边界的情况
        for( int i = 0; i < k; i++ )
            dp[0][i] = 1;
        a[0].push_back(0);
        for( int i = 1; i <= n; i++ ){
            int tmp = fun(i);//这行的状态
            for( int j = 0; j < k; j++ ){
                if(j & (j >> 1))continue;//判断同行是否相邻
                if(j & tmp)continue;//判断放羊是否合法
                a[i].push_back(j);
            }
            for( int j = 0; j < a[i].size(); j++ ){
                int u = a[i][j];
                for( int t = 0; t < a[i-1].size(); t++ ){
                    int v = a[i-1][t];
                    if(u & v)continue;//判断是否与上一行相邻
                    dp[i][u] = (dp[i][u] + dp[i-1][v]) % mod;
                }
            }
        }
        int ans = 0;
        for( int i = 0; i < k; i++ ){
            ans = (ans + dp[n][i]) % mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值