ZOJ 3769 Diablo III(DP)

81 篇文章 1 订阅

题意:给出13类装备,每种装备都有攻击力和防御值,每种装备只允许选择一个,求满足防御值至少在M的情况下,攻击力最大为多少.

有几个条件:13种装备里如果选择了双手装备,就不能选择武器和护盾了,还有戒指可以双手各带一个.

思路:首先不考虑条件,设dp[i][j]为前i种装备,防御值达到j的时候的最大攻击力.

那么可以有dp[i][j + t] = max(dp[i][j + t], dp[i - 1][j] + d)这里dp[i - 1][j]不等于-1,也就是必须先能达到这个防御值.

考虑双手装备的问题,可以把武器和护盾单独作为一件双手装备或者把两者合起来组合成一个双手装备,这样条件就没了.

考虑戒指的问题,选择单个戒指就相当于只有一只手戴戒指,把戒指两两组合起来也就相当于双手都带了戒指,所以条件就都没了.

不过这里的M比较大,直接从第一类装备开始递推可能会超时,由于种类之间没有关系所以从哪个种类开始递推都没有关系,所以这里可以把种类按照装备数量从大到小排序,把数量最大的种类作为递推起点,这里就可以省下很多时间,具体看代码.

#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include <memory.h>
#include <algorithm>
using namespace std;
const int MAX = 301;
struct Equip{
	int d, t;
	Equip(int dd = 0, int tt= 0){
		d = dd;
		t = tt;
	}
};

map<string, int>mp;
void init(){
	mp["Head"] = 1;
	mp["Shoulder"] = 2;
	mp["Neck"] = 3;
	mp["Torso"] = 4;
	mp["Hand"] = 5;
	mp["Wrist"] = 6;
	mp["Waist"] = 7;
	mp["Legs"] = 8;
	mp["Feet"] = 9;
	mp["Finger"] = 10;
	mp["Shield"] = 12;
	mp["Weapon"] = 13;
	mp["Two-Handed"] = 11;
}



bool cmp(const vector<Equip> & lhs, const vector<Equip> & rhs){
	return lhs.size() > rhs.size();
}
int main(int argc, char const *argv[]){
	vector<Equip> equip[14];
	int dp[12][50001];
	int T;
	init();
	scanf("%d", &T);
	while(T--){
		int N, M, ans = -1;
		scanf("%d%d", &N, &M);

		for(int i = 1; i <= 13; ++i){
			equip[i].clear();
		}

		for(int i = 1; i <= N; ++i){
			char name[12];
			int d, t, p;
			scanf("%s%d%d", name, &d, &t);
			p = mp[name];
			equip[p].push_back(Equip(d, t));
			if(p == 12 || p == 13){//把护盾和武器单个放在双手武器里.
				equip[11].push_back(Equip(d, t));
			}
		}

		vector<Equip> tmp;
		for(int i = 0; i < equip[10].size(); ++i){
			tmp.push_back(equip[10][i]);//只带单个戒指
			for(int j = i + 1; j < equip[10].size(); ++j){//带两个戒指
				tmp.push_back(Equip(equip[10][i].d + equip[10][j].d, equip[10][i].t + equip[10][j].t));
			}
		}
		equip[10].swap(tmp);
		tmp.clear();

		for(int i = 0;i < equip[12].size(); ++i){
			for(int j = 0; j < equip[13].size(); ++j){//把护盾和武器组合放到双手武器里
				equip[11].push_back(Equip(equip[12][i].d + equip[13][j].d, equip[12][i].t + equip[13][j].t));
			}
		}

		sort(equip + 1, equip + 12, cmp);//按数量从大到小排序
		memset(dp, -1, sizeof(dp));
	
		for(int i = 0; i < equip[1].size(); ++i){//数量最大的作为递推起点,省去很多时间(TLE->AC)
			Equip e = equip[1][i];
			int nm = e.t;
			if(nm > M)nm = M;
			dp[1][nm] = max(dp[1][nm], e.d);
		}
		for(int i = 2; i <= 11; ++i){
			int size = equip[i].size();//常数优化
			for(int j = 0; j <= M; ++j){
				if(dp[i - 1][j] != -1){
					dp[i][j] = max(dp[i][j], dp[i - 1][j]);
					for(int k = 0; k < size; ++k){
						Equip e = equip[i][k];
						int nm = j + e.t;
						if(nm > M)nm = M;
						dp[i][nm] = max(dp[i][nm], dp[i - 1][j] + e.d);
					}
				}
			}
		}

		printf("%d\n", dp[11][M]);
	}	
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值