【“盛大游戏杯”第15届上海大学程序设计联赛 G】【暴力模拟或状态DP高效做法】战斗

购买装备

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M

最近盛大的一款游戏传奇世界极其火爆。游戏玩家John,想购买游戏中的装备。已知游戏的商店里有n件装备,第i件装备具有属性值ai,购买需要花费bi个金币。John想去购买这些装备,但是账号中只有m个金币,John是个很贪婪的家伙,他想购买尽可能多的装备。并且在保证购买到最多件装备的情况下,他还想让他所购买的装备当中拥有最小属性值的装备属性值尽可能大

输入测试组数T,每组数据第一行输入整数n(1<=n<=100000)和m(1<=m<=109), 接下来有n行,第i行有两个数aibi(1<=ai,bi<=10000).

对于每组数据,输出两个数字,第一个数字代表John最多可以购买的装备数,第二个数代表在John购买最多件装备的前提下,所购买的装备当中拥有最小属性值的装备的最大属性值(输入数据保证至少可以购买一件装备)

1
2 4
3 2
2 3
1 3

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (a < b)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
struct A
{
	int hp, att;
}a[12], b[12], c[12], d[12];
int p[12];
bool fight()
{
	for (int i = 1; i <= n; ++i)a[i] = c[p[i]], b[i] = d[i];
	int pa = 1, pb = 1;
	while (pa <= n && pb <= n)
	{
		int akb = b[pb].hp / a[pa].att + (b[pb].hp % a[pa].att > 0);
		int bka = a[pa].hp / b[pb].att + (a[pa].hp % b[pb].att > 0);
		
		//AC
		if (akb == bka)
		{
			++pa; ++pb;
		}
		else if(akb < bka)
		{
			a[pa].hp -= akb * b[pb].att;
			++pb;
		}
		else
		{
			b[pb].hp -= bka * a[pa].att;
			++pa;
		}

		//TLE
		/*
		int mn = min(akb, bka);
		a[pa].hp -= mn * b[pb].att;
		b[pb].hp -= mn * a[pa].att;
		if (a[pa].hp <= 0)++pa;
		if (b[pb].hp <= 0)++pb;
		*/
	}
	return pa <= n;
}
bool solve()
{
	for (int i = 1; i <= n; ++i)p[i] = i;
	do
	{
		if (fight())return 1;
	} while (next_permutation(p + 1, p + n + 1));
	return 0;
}

struct P
{
	int o, hp;
	bool operator < (const P & b)const
	{
		if (o != b.o)return o < b.o;
		return hp > b.hp;
	}
};
bool got[11][1010][11];
P nxt[11][1010][11];	//nxt[i][j][k]表示我们已经现在杀到i(0base),剩余血量为j,如果用k来杀,能杀到的剩余血量值
P getnxt(int x, int y, int o)
{
	if (got[x][y][o])return nxt[x][y][o];
	got[x][y][o] = 1;

	int hp = a[o].hp;
	int att = a[o].att;
	while (x < n)
	{
		int akb = (y / att) + (y % att > 0);
		int bka = (hp / b[x].att) + (hp % b[x].att > 0);
		if (akb == bka)
		{
			return nxt[x][y][o] = { x + 1, b[x + 1].hp };
		}
		else if (akb < bka)
		{
			hp -= akb * b[x].att;
			y = b[++x].hp;
		}
		else
		{
			return nxt[x][y][o] = { x, y - att * bka };
		}
	}
	return nxt[x][y][o] = { n + 1, 0 };
};
P f[1024];
bool maskDP()
{
	for (int i = 0; i < n; ++i)
	{
		a[i] = c[i + 1];
		b[i] = d[i + 1];
	}
	MS(got, 0);
	int top = (1 << n) - 1;
	for (int i = 0; i <= top; ++i)
	{
		f[i] = { 0, b[0].hp };
	}
	for (int i = 0; i < top; ++i)
	{
		if (f[i].o >= n)
			return 1;
		int x = f[i].o;
		int y = f[i].hp;
		for (int j = 0; j < n; ++j)if (~i >> j & 1)
		{
			gmax(f[i | 1 << j], getnxt(x, y, j));
			if (f[i | 1 << j].o > n)
				return 1;
		}
	}
	return 0;
}

int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
		{
			scanf("%d%d", &d[i].hp, &d[i].att);
		}
		for (int i = 1; i <= n; ++i)
		{
			scanf("%d%d", &c[i].hp, &c[i].att);
		}
		
		//O(n! * n)
		//puts(solve() ? "YES" : "NO");

		//O(2 ^ n * n + n * n * 1000 * n)
		puts(maskDP() ? "YES" : "NO");
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】
http://acmoj.shu.edu.cn/problem/416/

【分析】
因为运算量的关系,有时候,要用比较清楚的、减少计算量的做法才能过。
而看起来很方便的写法,却可能会TLE。
然而这道题,实际有一个2 ^ n * n的做法
f[mask]表示我们死掉mask的人,可以对敌方造成的最大伤害(这是一个pair)

【时间复杂度&&优化】


*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章