hdu 2291 Five in a Row, Again 状态压缩搜索

题目地址

题目描述:(借鉴别人的

给两个n*n的矩阵,第一个代表一个人战胜一个人可以得到的经验值,第二个代表一个人战胜另一个人可以得到的分数,然后n个数,代表n个人的初始经验值,只有经验值大于对手才可以取胜,问第一个人最后取得的最大分数。

感悟:一开始根本看不懂这个题,最后看了多个博客,读了代码才知道其实就是暴力搜索,只是用了状态压缩(要不然直接模拟代码较长),感觉根本没什么技巧。

        还有我突然发现,原来题目的输入的两个矩阵,除了第一行最后都是没用的数据。   还有居然发现 【if(a<c+b) a=c+b;】  比 【d=c+b; if(a<d)a=d;】耗时,测试时好像是多用15MS。只是说一下也不用在意。

        此题本质就是用状态压缩,用一个数字表示 leader 与其余几人的对战情况(我的注释都描述成战斗了) , 所有状态以及每个状态最后一次该战胜谁都遍历求解,以达到最好的结果(其实这就是dp的状态转移吧)。 还有关于经验的计算一开始可以打表直接查询,但即使每次重新求一遍经验也能过,所以没有另外处理。

程序的注释写了不少,也算是第一次使用状态压缩吧!


#include<string.h>
#include<stdio.h>
#define MAXNUM 15
int dp[1<<MAXNUM];
int e[MAXNUM],w[MAXNUM],s[MAXNUM]; //通过题目发现,矩阵只有第一行有用,其余作废 
int main(){
	int t,n,i,j,k;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(i=0;i<n;i++)scanf("%d",&e[i]); for(i=n;i<n*n;i++)scanf("%d",&j);
		for(i=0;i<n;i++)scanf("%d",&w[i]); for(i=n;i<n*n;i++)scanf("%d",&j);
		for(i=0;i<n;i++)scanf("%d",&s[i]);
		memset(dp,0,sizeof(dp));
		//将DP用于直接计算的方式, 有的是将DP用于DFS的剪枝 
		int all_state=1<<(n-1); //获得了所有组合的状态总数 ,不包括第一人 
		for(i=0;i<all_state;i++){ //求i状态的dp值(遍历所有状态,包含了人数从小到大的过程*** ) 
			for(j=0;j<n-1;j++){ //leader 不算 
				if( i&(1<<j) ){ //这个状态下,新来这个选手参与和 leader 的比较 
					int tmp_i=i^(1<<j); //相同为 0 ,即考虑没和 j 比较前的状态
					int id=0;// 其实就是能打败的人的编号 
					int tmp=s[id];
					//下面这部分其实可以利用存储优化的 
					for(id=1;id<n-1;id++){
						if( 1&( tmp_i>>(id-1) )){ //与第一位判断就要位移 0  
							tmp+=e[id];//说明之前打过就攒下了经验 
						}
					}
					if(tmp>s[ j+1 ]){ //能打过 
						//只会发生在同层循环内,也就是打斗的先后循序不一样 
						tmp= dp[tmp_i] + w[j+1];  //就这里一个优化,能节省 15MS? 我试了半天 
						if( dp[i]< tmp ) dp[i]=tmp;
					}
				}
			}
		}
		printf("%d\n",dp[ all_state-1 ]); // 最后一个就是打完所有的最好的结果 
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值