poj 3254 Corn Fields【状态压缩dp】【入门第一题~~】

Corn Fields
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 17938 Accepted: 9447

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  

题意:给你一个n*m的矩形土地状态分布情况,0表示不能放牛,1表示能放牛,问使任意两只牛不相邻的方案数有多少,一只牛不放也算一种方案。

思路:可以用二进制去表示每行每列的放牛状态,0表示不能放牛,1表示放牛。我们可以提前将每行可能的状态情况枚举出来,由于题目要求不能两两相邻,所以在枚举每行时,就可以舍弃部分状态,如样例,满足题意的每行的可能状态有000,001,010,100,101,共5种,由于还要求两行之间也不能相邻,所以在枚举过程中,又可以舍弃部分状态,最后才进行状态转移,第i行第j种状态的方案数是由i-1行第j种状态的方案数递推而来,所以,状态转移方程为:dp[i][j] = dp[i][j]+dp[i-1][j]。

注意,题目要求进行取模,所以还要对10^8取模。这道题还比较坑的就是注意数组的数据范围~~

总结:和一般dp不同的是,状压存储的状态往往是一个集合,以目前我所能理解的就是,以十进制形式对状态压缩,以二进制形式保存集合状态(哈哈,个人理解),所以呢,目前个人感觉到的就是十进制和二进制之间的转换&&位运算的巧妙运用。

#include<stdio.h>
#include<string.h>
#define N 20
#define mod 100000000
int n,m,count;//n行m列,count存储满足两两不相邻的状态的总数 
int vis[N],ans[1000],dp[N][1000];
//vis存储每行实际0的占据情况,ans存储每种满足不相邻的状态

int Judge2(int x)
{
	if(x&x<<1)
		return 0;
	return 1;
 } 

void Init()
{
	count = 0;
	for(int i = 0; i < (1<<m);i++)//在列数为m的情况下,共有1<<m-1种分布情况 
	{
		if(Judge2(i))//找到其中满足两两不相邻的状态 
			ans[++count] = i;//并将状态i存入ans数组 
	}
	return;
}
int Judge(int x,int y)
{
	if(x&vis[y])//如果x状态和第y行的状态逆值相符,说明x状态不符合第y行的状态返回0 
		return 0;
	return 1;
}

int main()
{
	int number,sum;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		Init();//初始化 
		for(int i = 1; i <= n; i ++)
		{
			vis[i] = 0;//假设一行都能种植的状态为0,即将每行的状态赋为逆值 0(需要思考为什么不赋为1) 
			for(int j = 1; j <= m; j ++)
			{
				scanf("%d",&number);
				if(number == 0)//当有无法满足种植条件的 
				{
					vis[i] += (1<<(m-j));//将第i行第j列的状态累加存入数组vis(此处解释了为什么得赋为逆值)
					//(将十进制转为二进制会发现,二进制数满足第i行的状态分布) 
				}
			}
		}
		for(int i = 1; i <= count; i ++)
			if(Judge(ans[i],1))//如果有第i种状态和第一行的状态相符 
				dp[1][i] = 1;//第一行第i种状态的方案数初始化为1 
		for(int i = 2; i <= n; i ++)//从第二行开始往后递推 
		{
			for(int j = 1; j <= count; j ++)//枚举与第i行相符的状态 
			{
				if(!Judge(ans[j],i))
					continue;
				for(int k = 1; k <= count; k ++)//枚举与第i-1行相符的状态 
				{
					if(!Judge(ans[k],i-1))
						continue;
					if(ans[j]&ans[k])//如果第i行和第i-1行状态相冲突,舍去 
						continue;
					dp[i][j] = (dp[i][j] + dp[i-1][k])%mod;//第i行第j种状态的方案数+第i-1行第j种状态的方案数 
				}
			}
		}
		sum = 0;
		for(int i = 1; i <= count; i ++)//将最终每种状态的总方案数相加就是全部相符状态的总方案数 
			sum = (sum + dp[n][i]) % mod;
		printf("%d\n",sum);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值