POJ P3254 Java实现
参考文章:https://blog.csdn.net/mengxiang000000/article/details/51075506
题目链接:Corn Fields
对于给定的土地,令A为直到当前位置,牛可以放置的情况.A[i][j]为第i行第j状态.
A[i][j] += A[i-1][k].
因为 对于i行j列的A[i][j],需要考虑的是前一行的所有状态.
这里的j状态,是当前行占有的牛,作为二进制数字,转为整数时的值。
例如:
上面是草地分布,绿色为草地,黄色为不能长草的地.
假设计算出了A[2][10],A[2][2]的值,(实际上就是01010和00010两种放牛的状态)
那么对于第三行,
A[3][1],直到第三行最有一列放牛,牛可以放置的情况,
A[3][1] += A[2][2]+A[2][10]
同样的,第三行第三列和第五列放置牛的话(二进制状态为00101)
A[3][5] += A[2][2]+A[2][10].
所以,状态转移方程的形式就是:
A[i][j] += A[i-1][k].
但是不是所有土地都可以放牛,对于一块土地,牛能放置的方式,跟三个东西有关:
1.当前行的上一行,相同的列,有没有牛在吃草
2.当前行有没有相邻的两个牛一起吃草
3.当前行放置的牛所在的草地上是否有草
所以在写状态转移的时候,需要根据这三个条件去判断.
这里提供二进制的方式去判定三种状态.
情况1:前行的上一行,相同的列,有没有牛在吃草
假设绿色的是放牛的区域,黄色的是没有放牛的区域.
对比相邻两行的两种放置方式AB和CD.
将它们看成二进制数:
A:1100
B:0011
C:1011
D:1011
可以使用与运算来判断相邻两行同一列是否都放置了牛(颜色相同就是都放置了牛)
可以使用[A&B==0?符合规则:不符合规则]来进行放置是否符合规则的判断.
情况2当前行有没有相邻的两个牛一起吃草
考虑A和C两种情况.(先忽略蓝色的格子)
A:1011
C:1010
考虑以下左移操作.
所以就和上图的B和D一样,在进行左移后,AB在同一列有相同的绿色格子,但是CD在同一列没有相同的绿色格子.
如果二进制数有两位连续相同的1的话,将二进制左移一位后,和它本身进行与操作,一定会有一位是1.
所以对于数字M,判断是否有连续的1,可以使用[(M<<1)&M ==1?包含连续:不含连续]来判断.
情况3当前行放置的牛所在的草地上是否有草
在放置牛的时候,如何保证牛放置的地方一定就是有草的?
上图草地为11010
现在有三种牛的放置情况
A:10101
B:11000
C:11010
直观去看,只有绿色草地能放牛,所以A放置方式不符合规则,BC放置方式符合规则.
符合规则说明牛的位置必须是草,所以仍旧可以利用与运算.
加入需要判断的数字为A,比较对象为B:
[A&B==A?符合条件:不符合条件]
使用它可以判断以当前方式放牛是否符合土地规则.
以上就是分析,下面是JAVA代码.
在代码中,动态规划数组初始化我增加了第0行,实际计算是从dp[1][0]~dp[n][1<<m]的.
注意,第二维是将土地二进制转为十进制后的情况枚举.
为何要初始化dp[0][0]=1?
因为题目中说即使不放牛,也算一种方式.所以即使是整行都是0,也算一种情况.
为什么需要第0行?
因为需要用它来计算起始行(第一行)的状态.第0行全部为0,所以第0行是不会影响第一行的结果的.
如果不容易去理解,也可以手动去计算以下第一行的状态作为初始状态.
package judge._open_judge.P3254_CornFields;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main2 {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int m = Integer.parseInt(st.nextToken());
int n = Integer.parseInt(st.nextToken());
int[] arrays = new int[m+1];
int mod = 100000000;
int sum;
for(int i = 1;i<=m;i++){
st = new StringTokenizer(br.readLine());
sum = 0;
for(int j = 0;j<n;j++){
sum+=Math.pow(2,n-j-1)*Integer.parseInt(st.nextToken());
}
arrays[i] = sum;
}
long[][] dp = new long[m+1][(1<<n)];
dp[0][0] = 1;
for(int i = 1;i<=m;i++){
//当前行的🐂的状态
for(int j = 0;j<(1<<n);j++){
//前一行的🐂的状态
//🌽种在土地上,牛只能在土地上吃玉米
if((j&arrays[i])!=j){
continue;
}
//🐂左右不能相邻
if((j&(j<<1))!=0){
continue;
}
for(int k = 0;k<(1<<n);k++){
//🐂上下不能相邻
if((j&k)!=0){
continue;
}
//当前行的状态和=当前行的状态+前一行的状态和
dp[i][j] += dp[i-1][k];
dp[i][j] = dp[i][j]%mod;
}
}
}
long result = 0;
for(int i = 0;i<(1<<n);i++){
result+=dp[m][i];
result %= mod;
}
System.out.println(result);
}
}