P1879 玉米田Corn Fields题解
洛谷 P1879 链接: .
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
input
2 3 1 1 1 0 1 0
output
9
看到两个数M N都小于12,第一反应就是状压,用二进制来表示每一行的状态,例如,j=13时,j 用二进制表示为 1101 ,表示1号位置种草,2号位置种草,3号位置不种草,4号位置种草。
首先输入M N ,然后输入地图,这里有一种快捷方式记录地图,相当于把每一行的状态压缩为一个数字存储,列如样例第一行 1 1 1 压缩为 7 ,这样会使后面的操作简单些
for(int i=1;i<=m;i++) for(int j=0;j<n;j++){ int x; cin>>x; mp[i]|=(x<<j);//记录地图信息 }
本题有两个约束条件,第一是不能 0 位置种草,第二时不能有连续的草丛
先解决第一个条件,由于之前已经记录了压缩后的地图状态了,假设此时第 i 行地图状态为mp[i] ,第i 行的状态为k , 假设 mp[i] 二进制的第 j 位为0,那么k的第 j 位就只能是0,如果 mp[i] 第 j 位为1,那么k的第 j 位可以是 0或1,这里我们用一种运算—— |,或运算 用 (mp[i] | k) == mp[i] 来判断合法 画图理解下 第二个条件比较简单,设当前行状态为 j ,上一行状态为 k j & ( j >>1 )可以快速判断是否有连续的 1 j & k 可以快速判断是否与上一行状态有冲突
两个条件都满足时候 转移状态 dp[i] [j] += dp[i-1] [l]
附上代码
#include<bits/stdc++.h> using namespace std; int n,m,sum=0,mod=1e9; int mp[105]={ 0},dp[13][1<<12];//mp存储地图情况,dp枚举每一种状况 int main(){ cin>>m>>n; for(int i=1;i<=m;i++) for(int j=0;j<n;j++){ int x; cin>>x; mp[i]|=(x<<j);//记录地图信息 } for(int l=0;l<(1<<n);l++){ if((l&(l>>1))) continue; if((mp[1]|l)!=mp[1]) continue; dp[1][l]=1; }//初始化第一行的情况 for(int i=2;i<=m;i++){ for(int j=0;j<(1<<n);j++){ //当前行 int flag=1; if((j&(j>>1))) continue;//判断当前行是否有相邻的情况 if((mp[i]|j)!=mp[i]) continue;//判断是否在0的位置种草了 for(int l=0;l<(1<<n);l++){ if((l&(l>>1))) continue;//判断上一行是否有相邻的情况 if((mp[i-1]|l)!=mp[i-1]) continue; if(l&j) continue; //判断此行和上一行是否相邻 dp[i][j]+=dp[i-1][l]; dp[i][j]%=mod; } } } for(int i=0;i<(1<<n);i++){ sum+=dp[m][i]; sum%=mod; } cout<<sum; return 0; }