【题解】方格取数

题目

题目描述 Problem Description

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

输入 Input

包括多个测试实例,每个测试实例包括一个整数 n n n × n n\times n 个非负数( n 20 n\leq 20 )

输出 Output

对于每个测试实例,输出可能取得的最大的和

样例输入 Sample Input

3
75 15 21 
75 15 28 
34 70 5 

样例输出 Sample Output

188

题解

思路

我们可以自上而下、一行行地选择格子。在一行内选择格子的时候,只和上一行的选择方案有关,我们就可以将” 当前放到第几行、当前行的选择方案”作为状态进行动态规划。

这里,我们就要用到状态压缩: 一行里被选择的格子实际上是一个集合, 我们要将这个集合压缩为一个整数。比如,对于一个 3 3 列的矩阵,如果当前行的状态是 ( 5 ) 10 = ( 101 ) 2 (5)_{10} = (101)_2 ,那么就意味着当前行选择了第一 个和第三个格子;类似地,如果当前行的状态是 ( 6 ) 10 = ( 110 ) 2 (6)_{10} = (110)_2 ,那么就意味着当前行选择了第-个和第二个格子 (当然由于计算顺序的缘故,我们通常会把 ( 110 ) 2 (110)_2 当做选择倒数第一个和倒数第二 个格子, 这并不影响我们理解这道题的解法)。

实现

如果上一行的状态是now, 下一行的状态是prev, 那么我们只需要确保上下两行的选择方案里没有重复的元素,也就是(now & prev)==0就可以了。

此外,我们还需要判断当前行的状态是否合法,因为读入的矩阵中并不是每个格子都可以选择的,如果我们将矩阵中每行的值也用状态压缩来存储,不妨记为flag,那么当前行选择的格子集合一定包含于当前行合法格子的集合,也就是说,(now | flag) == flag必须成立;同时行内也不能选择相邻的,也就是now & (now >> 1) ==0必须成立

这样,我们就可以通过枚举上一 行的所有状态,来更新当前行、当前状态的最优解了。直到算完最后一行,统计一下所有状态的最大值即可。

代码:

#include<iostream>
#include<cstdio> 
using namespace std;
int a[21][20];
int state[21];//初始每行的状态
int dp[21][1<<20];
bool ok(int now){//判断行内是否相交
	return(now&(now>>1))==0;
}
bool fit(int now,int i){//用来判断now这个选取状态是否和符合第i行的输入
	return(now | state[i])==state[i];
}
bool not_intersect(int now, int prev){//判断状态now和状态prev是否能放在相邻的行
	return(now&prev)==0;
}
int count(int now){//统计now状态选了多少个元素
	int s=0;
	while(now){
		s+=(now & 1);
		now>>=1;
	}
	return s;
}

int main(){
	int n, m;
	scanf("%d %d",&n,&m);
	//行从1开始,列从0开始,这样后面处理起来方便
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			cin >> a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			if(a[i][j]){
				state[i]+=(1 << j);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1<<m);j++){
			if(ok(j) && fit(j,i)){
				for(int k=0;k<(1<<m);k++){
					if(ok(k)&&fit(k,i-1) && not_intersect(j,k)){
						dp[i][j]=max(dp[i][j],dp[i-1][k]+count(j));
					}
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<(1<<m);i++){
		ans=max(ans,dp[n][i]);
	}
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/tanfuwen_/article/details/106997103