【“盛大遊戲杯”第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)

【時間複雜度&&優化】


*/


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