[poj3254]Corn Fields_状压dp

Corn Fields poj3254

    题目大意:给你一个n*m的地,每一块地可以种或不种,两块种过的地不能挨着,可以一块都不种,问所有的种地方案数。

    注释:读入用0和1,1<=n,m<=12.

      想法:这题和炮兵阵地特别像,比炮兵更简单。我们再度入的时候直接处理出当前行的地的不可种的情况。预处理出一行如果都能种的话所可能的方案数。此处需要满足的就是两块地不能挨着,通过打表我们可以发现这种情况最多只有377种情况(我们附上打表用的程序)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
bool check(int x)//判断当前状态是否可行
{
	if(x&(x<<1)) return false;
	return true;
}
int cnt;
void before(int mid)//处理所有状态
{
	for(int i=0;i<(1<<mid);i++)
	{
		if(check(i)) cnt++;
	}
}
int main()
{
	int n;
	cin >> n;
	before(n);
	printf("%d\n",cnt);//输出所有可行状态数
	return 0;
}

      然后,我们先预处理出第一行,怎么处理呢?其实map是0的情况,也就是读入数据的反码。我们对于一个状态只需要通过&上map对应的下标就可以判断当前数据是否合法。如果合法,dp[1][i]就是1。其中,dp[i][j]表示第 i 行状态为 j ,前 i 行能填充的方案数。转移时,我们通过外层松弛行数,内层枚举所有状态,用&判断即可。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 1000000000
using namespace std;
int dp[15][380];
int map[15];//存储的是反码
int str[380];//存储所有状态
// int sum[350];
int cnt;//状态数目
bool check(int x)//判断当前状态是否可行
{
	if(x&(x<<1)) return false;
	return true;
}
// int getSum(int x)
// {
// 	int ans=0;
// 	while(x>0)
// 	{
// 		if(x&1) ans++;
// 		x>>=1;
// 	}
// 	return ans;
// }
void before(int mid)//预处理所有状态
{
	for(int i=0;i<(1<<mid);i++)
	{
		if(check(i))
		{
			str[++cnt]=i;
			// sum[cnt]=getSum(i);
		}
	}
}
int main()
{
	int n,m;
	cin >> n >> m;
	int a;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d", &a);
			if(a==0) map[i]|=(1<<(j-1));//运用|运算的性质来构造反码
		}
	}
	before(m);//预处理
	// cout << "cnt = " << cnt << endl;
	for(int i=1;i<=cnt;i++)
	{
		if(!(str[i] & map[1]))
		{
			dp[1][i]=1;
		}
	}
	// for(int i=1;i<=n;i++) printf("%d ",map[i]);
	// puts("");
	// for(int i=1;i<=cnt;i++) printf("%d ",dp[1][i]);
	// puts("");
	for(int i=2;i<=n;i++)//转移
	{
		for(int j=1;j<=cnt;j++)//枚举i的状态
		{
			if(str[j] & map[i]) continue;//判断状态是否合法
			for(int k=1;k<=cnt;k++)//枚举i-1的状态
			{
				if(str[k] & map[i-1]) continue;
				if(str[j] & str[k]) continue;
				dp[i][j]+=dp[i-1][k];
			}
		}
	}
	int ans=0;
	for(int i=1;i<=cnt;i++)
	{
		if(map[n]&str[i]) continue;
		ans+=dp[n][i];
		ans%=mod;//重要,不加luogu会WA 10%
	}
	printf("%d\n",ans);
	return 0;
}

    小结:第2道状压。调试时候不要忘记题目所求的,在发现一个题与另一个题相似时不要被另一道题所局限

原文地址:https://www.cnblogs.com/ShuraK/p/8569557.html

时间: 2024-10-04 15:45:54

[poj3254]Corn Fields_状压dp的相关文章

poj 3254 Corn Fields 状压dp入门

// poj3254 状压dp入门 // dp[i][S]表示前i行第i行状态为S时放牧的情况 // S为十进制的二进制数形式如5为101表示第一列和第三列放牧的方法数 // 首先dp[0][S]合法的话就是1 // 状态转移方程为dp[i][S] = sigma(dp[i-1][V])(S与V是合法状态) // 最后的结果就是dp[n][S](S为所有的合法状态) // // 刚开始十分傻x的dp[0][S]置为get(S),...get(S)是其中1的个数 // 这又不是求放羊的数量,这是方

POJ 1684 Corn Fields(状压dp)

描述 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

poj3254:基础状压dp

第二个状压dp 做过的第一个也是放牛问题,两头牛不能相邻 这个题多了一个限制,就是有些位置不能放牛 于是先与处理一下每一行所有不能放牛的状态,处理的过程直接对每一个不能放牛的状态或以下 ac代码: #include <iostream> #include <stdio.h> #include<string.h> #include<algorithm> #include<string> #include<ctype.h> using n

POJ 3254 Corn Fields 状压DP

链接:http://poj.org/problem?id=3254 题意:一块M*N的田地,每小块地大小是1*1,可以种植物的标记为1,不可以种植物的标记为0,并且相邻的两块地不可以同时种植物.问题是有多少种不同的种植方案(所有地都不种也是一种种植方案) 思路:这是第一道状压DP题,从第一行更新到最后一行,每一行用一个N位的二进制数来表示该行的状态1表示该位置种了植物,0表示该位置没种植物.因为每块地只对相邻的土地能否种植有所影响,所以每一行的状态可以用前一行的状态递推得到. 资料:http:/

[ An Ac a Day ^_^ ] POJ 3254 Corn Fields 状压dp

题意: 有一块n*m的土地 0代表不肥沃不可以放牛 1代表肥沃可以放牛 且相邻的草地不能同时放牛 问最多有多少种放牛的方法并对1e8取模 思路: 典型的状压dp 能状态压缩 能状态转移 能状态压缩的题的特点就是只有两种状态 所以用0 1表示两种状态 用位运算判断是否符合条件 然后将前一行的合理状态转移到后一行 最后统计最后一行的状态 dp[i][j]代表第i行以第j种状态放牛时有多少种不同的状态 (c++的语言特性是 封装 继承 多态~) 1 /* ***********************

题解——[USACO06NOV]玉米田Corn Fields 状压DP

题面: 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用. 遗憾的是,有些土地相当贫瘠,不能用来种草.并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边. John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案) 输出一个整数,即牧

P1879 [USACO06NOV]玉米田Corn Fields 状压dp

链接在此! 正解:状压dp(emm……据说插头dp也可以趴但我不管!!!不会!!! 解题报告: ……我真的太菜了……我以为一个小时前要搞完的题目调错误调了一个小时……90分到100我差不多搞了一个小时…… 然后这题还是做过的……就很气,觉得确实是要搞下博客没事儿复习下不然做过的题目还花俩小时我真的哭死…… 先放上错误的90分代码讲一下错哪儿了(因为……其实100并不难是可以想到的……没有太大讲的意义,主要我太菜了所以才会搞这么久TT 点我♂看♂沙雕灵巧在线WA题 然后错误的点是最后一个点RE,开

[USACO06NOV]玉米田Corn Fields (状压$dp$)

题目链接 Solution 状压 \(dp\) . \(f[i][j][k]\) 代表前 \(i\) 列中 , 已经安置 \(j\) 块草皮,且最后一位状态为 \(k\) . 同时多记录一个每一列中的不能放的位置 \(w[i]\). 然后就可以很轻松的转移了... 转移方程看代码. Code #include<bits/stdc++.h> #define ll long long using namespace std; ll f[13][145][10000],n,K; ll js[1000

POJ 3254 Corn Fields (状压DP+滚动数组)

题目地址:POJ 3254 状压水题. 先预处理出每行所有可能出现的情况.然后可以用vector存起来. 然后先处理出第一行所有的情况.然后再从第二行开始不断与上一行进行状态转移,状态转移很简单就不说了. 最后统计出最后一行的个数和就可以了. 代码如下; #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #