(POJ 3254)Corn Fields 状态压缩DP 好题

2024-02-05 02:32

本文主要是介绍(POJ 3254)Corn Fields 状态压缩DP 好题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Corn Fields
Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can’t be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers: M and N
Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)
Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.
Sample Input

2 3
1 1 1
0 1 0
Sample Output

9
Hint

Number the squares as follows:
1 2 3
4

There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.
Source

USACO 2006 November Gold

题意:
有n*m大的一个地方,1表示土地肥沃可以种植物,0表示不能种植物,问:在不许有两个植物相邻的情况下,有多少种放置的方法。

分析:
刚开始做状压DP的题,对于这题而言我只能想到超时的DFS。
看了这边博客才搞懂的:http://blog.csdn.net/mengxiang000000/article/details/51075506
但是我也是啃了一个小时才完全搞懂。下面是我自己根据这篇博客做的思路整理

首先对于一个12*12的网格,每一行的选择可以有很多,为了方便表示出每一行选择的状态,我们将每一行压缩在一个int中,每一位二进制表示一个格子的状态(1,0)。
比如样例1变成了a[2] = {7,2}
然后题目要求没有两个所选的格子有相邻的边。我们从第一行开始往下依次选出每行可选的所有情况,那么我们只需要注意两点即可:(比如我们再选第i行)
1:第i行所选的格子一定是a[i]中二进制为1的位置的子集,并且没有1相邻。
2:第i行和第i-1行没有相邻的1被同时选中。

对于条件1:
判断(i,j)是否合法,首先我们知道,j的状态和第i行土地的状态的0的位子是一定相同的,但是j的1可以比土地的少,我们举例说明:
假设土地的状态值为5:1 0 1,我们合法的j放置状态有: 1 0 0 / 1 0 1 /0 0 1
对于土地状态我们对合法j放置状态进行&运算有:

  1 0 1
& 1 0 0
----------------
  1 0 0   1 0 1
& 1 0 1
------------------
  1 0 1  1 0 1
& 0 0 1
-----------------
  0 0 1

我们发现,如果是合法的放置状态,我们用土地的状态值&j的状态值的结果一定等于j。辣么我们对于j是否合法的第一个判断就要这样写(反例大家随便写一个就发现确实不等于j):
if(a[i]&j!=j)return 0;
对于另外一个需要判断的条件:同一行不能有两个相邻的1,解决的方法就是在2进制01串的末尾加上一个0之后和原来的01串进行&运算,如果结果为0,合法,否则不合法,
我们也举例说明:

       1 0 1
   & 1 0 1 0
---------------------
     0 0 0 0合法
    1 1 0
& 1 1 0 0
----------------------
  0 1 0 0不合法

对于这两个判断我们已经了解如何操作,然后我们再用代码来实现:

int judge(int x,int y)
{if((a[x] & y) != y) return 0; // 判断是第i行可取的子集if((y & (y<<1)) != 0) return 0; //判断没有相邻的1return 1;
}

对于条件2:
只要 (j & k) == 0 那么就满足条件

对于所有满足条件的k我们就知道从i-1行的k状态可以到达第i行的j状态,即dp[i-1][k] ->dp[i][j],我们记录所有到达dp[i][j]状态的数目。那么dp[n][i]的和即为我们所要求的解。

对于样例dp[][]的所有状态如下:
这里写图片描述

整体dp思路和01背包一样

AC代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define mod 100000000
int n,m,a[15];
int dp[13][1<<13];int judge(int x,int y)
{if((a[x] & y) != y) return 0; // 判断是第i行可取的子集if((y & (y<<1)) != 0) return 0; //判断没有相邻的1return 1;
}void solve()
{memset(dp,0,sizeof(dp));dp[0][0] = 1;  // 初始化for(int i=1;i<=n;i++){for(int j=0;j<(1<<m);j++) //判断在第i行放置j这种状态的牛是否合法{if(judge(i,j)==0) continue;for(int k=0;k<(1<<m);k++) //找出所有第i-1行和第i行不冲突的状态{if((j&k) != 0) continue; //判断是否冲突dp[i][j] += dp[i-1][k];dp[i][j] %= mod;}}}int ans = 0;for(int i=0;i<(1<<m);i++){ans += dp[n][i];ans %= mod;}printf("%d\n",ans);
}
int main()
{while(scanf("%d%d",&n,&m)!=EOF){int k;for(int i=1;i<=n;i++){a[i] = 0;for(int j=1;j<=m;j++){scanf("%d",&k);a[i] = (a[i]<<1) + k; //转化为2进制表示}}solve();}return 0;
}

这篇关于(POJ 3254)Corn Fields 状态压缩DP 好题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/679521

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M