2019-02-25-方格取数(1)-HDU1565-dp-状态压缩

HDU-1565-方格取数(1)

Problem Description

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


Input

包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)

Output

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


Sample Input

3

75 15 21

75 15 28

34 70 5

Sample Output

188

题目简述

利用状态压缩,把每一行的一种取数方法压缩到一个二进制数,然后对于每行的cnt种取数方法,从第1行到第n行进行dp求解

每一行的dp最大值都和上一行的各种取数情况下的dp值有关

状态压缩:每一行有n个格子,将其对应于2^{n}个数,转换成二进制后每一位上,1表示取该数,0表示不取该数。

位运算:a&b,如果某一位上都为1,则结果中该位也为1,否则为0

states[M]:每一种状态对应的取数方法(对于确定的i,states也是确定的)

andans[N][M]:第i行第j种情况下,第i行的总值

dp[N][M]:第i行第j种情况下,第1行到第i行的总值

状态转移方程dp[i][j]=max(dp[i][j],dp[i-1][k]+andans[i][j])

重要雷区:运算优先级!!! (a&b)==0和a&b==0不一样!!!

条目检索dp(动态规划),状态压缩,位运算

输入输出

输入:无难点

输出:无难点

使用模板(无)

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 22
#define M 30000
using namespace std;
int states[M];
int andans[N][M];
int dp[N][M];
int mp[N][N];
int cnts;
void find_states(int n){    //寻找每个n对应的情况总数,并把相应可选的情况记入数组
	int sum=1<<n;           //相当于2^n
	cnts=1;
	for(int i=0;i<sum;i++){
		if((i&(i<<1))==0){  //如果情况i中没有两个相邻数位都是1的情况,就记入数组
			states[cnts++]=i;
		}
	}
}
void read(int n){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&mp[i][j]);
		}
	}
}
void init(){
	memset(states,0,sizeof(states));
	memset(dp,0,sizeof(dp));
	memset(andans,0,sizeof(andans));
}
void sum_row_states(int n,int cnt){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=cnt;j++){            //对每种情况遍历
			int sum=n;
			for(int k=1;k<(1<<n);k=(k<<1)){
				if((k&states[j])!=0){       //如果第sum位上是1,则把值加入andans[i][j]中
					andans[i][j]+=mp[i][sum];
				}
				sum--;
			}
		}
	}
}
int dp_f(int n,int cnt){                    //主算法
	for(int i=1;i<=cnt;i++)dp[1][i]=andans[1][i];
	for(int i=2;i<=n;i++){
		for(int j=1;j<=cnt;j++){
			for(int k=1;k<=cnt;k++){
				if((states[j]&states[k])==0){     //第i行的j情况和i-1行的k情况符合条件
					dp[i][j]=max(dp[i][j],dp[i-1][k]+andans[i][j]);
				}
			}
		}
	}
	int sum=0;
	for(int i=1;i<=cnt;i++){
		sum=max(sum,dp[n][i]);
	}
	return sum;
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
        //预处理
		init();
		find_states(n);
		read(n);
		sum_row_states(n,cnts);
        //计算
		int ans=dp_f(n,cnts);
		printf("%d\n",ans);
	}
	return 0;
}

参考链接

https://blog.csdn.net/u011077606/article/details/43487421

https://blog.csdn.net/a1046765624/article/details/78767347

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值