购买装备
发布时间: 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 行有两个数 ai , bi ( 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)
【时间复杂度&&优化】
*/