JZOJ3404[NOIP2013模拟]卡牌游戏(2019.08.04[NOIP提高组]模拟 B 组T2)
卡牌游戏 (Standard IO)
Time Limits: 1000 ms Memory Limits: 524288 KB Detailed Limits
Time to Submit: 01:59:17
Description
小X 为了展示自己高超的游戏技巧,在某一天兴致勃勃地找小Y 玩起了一种卡牌游戏。每张卡牌有类型(攻击或防御)和力量值两个信息。
小Y 有n 张卡牌,小X 有m 张卡牌。已知小X 的卡牌全是攻击型的。
游戏的每一轮都由小X 进行操作,首先从自己手上选择一张没有使用过的卡牌X。如果小Y 手上没有卡牌,受到的伤害为X 的力量值,否则小X 要从小Y 的手上选择一张卡牌Y。若Y 是攻击型(当X 的力量值不小于Y 的力量值时才可选择),此轮结束后Y 消失,小Y 受到的伤害为X 的力量值与Y 的力量值的差;若Y 是防御型(当X 的力量值大于Y 的力量值时才可选择),此轮结束后Y 消失,小Y 不受到伤害。
小X 可以随时结束自己的操作(卡牌不一定要用完)。希望聪明的你帮助他进行操作,使得小Y 受到的总伤害最大。
Input
输入的第一行包含两个整数n 和m 。
接下来n 行每行包含一个字符串和一个整数,分别表示小Y 的一张卡牌的类型(“ATK”表示攻击型,“DEF”表示防御型)和力量值。
接下来m 行每行包含一个整数,表示小X 的一张卡牌的力量值。
Output
输出一行包含一个整数,表示小Y 受到的最大总伤害。
Sample Input
输入1:
2 3
ATK 2000
DEF 1700
2500
2500
2500
输入2:
3 4
ATK 10
ATK 100
ATK 1000
1
11
101
1001
Sample Output
输出1:
3000
【样例说明1】
第一轮,小X 选择自己的第一张卡牌和小Y 的第二张卡牌,小Y 的第二张卡牌消失。
第二轮,小X 选择自己的第二张卡牌和小Y 的第一张卡牌,小Y 的第一张卡牌消失,同时受到500 点伤害。
第三轮,小X 选择自己的第三张卡牌,此时小Y 手上已经没有卡牌,受到2500 点伤害。
小X 结束游戏,小Y 共受到3000点伤害。
输出2:
992
【样例说明2】
第一轮,小X 选择自己的第三张卡牌和小Y 的第一张卡牌,小Y 的第一张卡牌消失,同时受到91点伤害。
第二轮,小X 选择自己的第四张卡牌和小Y 的第二张卡牌,小Y 的第二张卡牌消失,同时受到901点伤害。
小X 结束游戏,小Y 共受到992点伤害。
Data Constraint
各规模均有一半数据满足小Y 只有攻击型卡牌。
对于30%的数据,1≤ n,m ≤ 6。
对于60%的数据,1≤ n,m ≤10^3。
对于100%的数据,1≤ n,m ≤10^5,力量值均为不超过10^6的非负整数。
一、暴力搜索(30)
这个题目前能保证正确的貌似是这个搜索,然而它是会TLE的。
#include<bits/stdc++.h> using namespace std; inline int poread() { char c = 0; int res = 0; while(!isdigit(c = getchar())); do { res = res * 10 + c - 48; } while(isdigit(c = getchar())); return res; } const int MAXN = 1e5 + 5; int n,m; struct node { bool isstack; int force; }Y[MAXN]; int X[MAXN]; bool v_Y[MAXN],v_X[MAXN]; int ans; void dfs(int now,int cnt) { ans = max(ans,now); // cerr<<cnt<<":"<<now<<" "<<x<<" "<<y<<endl; // for(register int i = 1; i <= n; ++i) // cerr<<v_Y[i]; // cerr<<endl; // for(register int i = 1; i <= m; ++i) // cerr<<v_X[i]; // cerr<<endl; if(cnt > m ) { return; } int sum = now; if(cnt >= n) { for(register int j = 1; j <= m; ++j) { if(!v_X[j]) { sum = now; v_X[j] = true; sum += X[j]; dfs(sum, cnt + 1); v_X[j] = false; } } } else { for(register int i = 1; i <= n; ++i) { if(!v_Y[i]) { for(register int j = 1; j <= m; ++j) { if(!v_X[j]) { sum = now; if(Y[i].isstack) { if(X[j] < Y[i].force) continue; sum += (X[j] - Y[i].force); } else { if(X[j] <= Y[i].force) continue; } v_Y[i] = true, v_X[j] = true; dfs(sum,cnt + 1); v_Y[i] = false, v_X[j] = false; } } } } } } int main() { #ifdef lky233 freopen("testdata.in","r",stdin); freopen("testdata.out","w",stdout); #endif n = poread(); m = poread(); for(register int i = 1; i <= n; ++i) { char s[10]; scanf("%s",s); Y[i].force = poread(); Y[i].isstack = (s[0] == 'A'); } for(register int i = 1; i <= m; ++i) { X[i] = poread(); } dfs(0,0); cout<<ans<<endl; }
二、出锅的贪心
这个贪心策略是这样的,首先对于这个问题,有两种策略:
1、直接攻击对方的攻击牌,拿到分数后结束游戏。
师曰:对于这个策略,我们将x从大到小排序,y从小到大排序,一直攻击直到结束。
对于样例2,该策略可以通过,而且原本是A了这个题的。
Hack:
2 3
ATK 599
ATK 1000
1200
600
450
显然如果使用上述策略,ans = 601,但是1200->1000,600->599,450->脸,会获得651的分数。
2、拿光对手的所有牌,并用自己的牌打对方脸直接攻击。
对于这个策略,应该用最小的消耗解除掉对答案没有产生贡献的盾牌,之后使用策略1攻击攻击牌,最后打脸。
对于出锅的数据,采用了把y从大到小排一遍进行计算然后取max的方式过掉了。(面向数据编程大法)
#include<bits/stdc++.h> using namespace std; #define int long long inline int poread() { char c = 0; int res = 0; while(!isdigit(c = getchar())); do { res = res * 10 + c - 48; } while(isdigit(c = getchar())); return res; } const int MAXN = 1e5 + 7; int n,m; int ans_1,ans_2,ans_3; struct node { int x; bool isstack; void in() { char s[10]; scanf("%s",s); isstack = (s[0] == 'A'); x = poread(); } }datax[MAXN],datay[MAXN],x[MAXN],y[MAXN],tmpdata[MAXN]; bool cmp1(node a,node b)//从小到大 { return a.x < b.x; } bool cmp2(node a,node b)//从大到小 { return a.x > b.x; } void tan_1()//只打掉Attack { int tmpans_1 = 0,tmpans_2 = 0; int tmpm = 0,tmpn = 0; memset(x,0,sizeof(x)); memset(y,0,sizeof(y)); memcpy(x,datax,sizeof(datax)); for(register int i = 1;i <= n; ++i) { if(datay[i].isstack) y[++tmpn] = datay[i]; } sort(y+1,y+tmpn+1,cmp1); sort(x+1,x+m+1,cmp2); int nn = min(tmpn,m); for(register int i = 1; i <= nn; ++i) { if(x[i].x < y[i].x) break; tmpans_1 += x[i].x - y[i].x; } sort(y+1,y+tmpn+1,cmp2); for(register int i = 1; i <= nn; ++i) { if(x[i].x < y[i].x) break; tmpans_2 += x[i].x - y[i].x; } ans_1 = max(tmpans_1,tmpans_2); } bool v[MAXN]; void tan_2() { if(n > m) { ans_2 = 0; return; } int tmpans_1 = 0,tmpans_2 = 0; memset(x,0,sizeof(x)); memset(y,0,sizeof(y)); for(register int i = 1; i <= m; ++i) { tmpdata[i] = datax[i]; } int tmpm = 0,tmpn = 0; for(register int i = 1; i <= n; ++i) { if(!datay[i].isstack) y[++tmpn] = datay[i]; } //双指针,破盾 sort(tmpdata+1,tmpdata+m+1,cmp1); sort(y+1,y+tmpn+1,cmp1); int point1 = 1,point2 = 1,tmp_cnt = 0; while(point1 <= m && point2 <= tmpn) { // cerr<<point1<<endl; while(tmpdata[point1].x <= y[point2].x) { if(point1 > m) break; ++point1; } if(tmpdata[point1].x > y[point2].x) ++tmp_cnt,v[point1] = true; ++point2; ++point1; } // cerr<<1<<endl; //不能打破所有的盾,这个策略不可行。 if(tmp_cnt < tmpn) { ans_3 = 0; return; } tmpn = tmpm = 0; for(register int i = 1; i <= m; ++i) { if(!v[i]) x[++tmpm] = tmpdata[i]; } memset(y,0,sizeof(y)); for(register int i = 1; i <= n; ++i) { if(datay[i].isstack) y[++tmpn] = datay[i]; } sort(y+1,y+tmpn+1,cmp1); sort(x+1,x+tmpm+1,cmp2); int nn = min(tmpn,tmpm),mm = max(tmpn,tmpm); #ifdef lky233 for(register int i = 1; i <= tmpm; ++i) cerr << x[i].x << endl; for(register int i = 1; i <= tmpn; ++i) cerr << y[i].x << endl; #endif bool con = 1; for(register int i = 1; i <= nn; ++i) { if(x[i].x < y[i].x) { con = 0; break; } tmpans_1 += x[i].x - y[i].x; } if(con) for(register int i = nn + 1; i <= mm; ++i) { tmpans_1 += x[i].x; } con = 1; sort(y+1,y+tmpn+1,cmp2); for(register int i = 1; i <= nn; ++i) { if(x[i].x < y[i].x) { con = 0; break; } tmpans_2 += x[i].x - y[i].x; } if(con) for(register int i = nn + 1; i <= mm; ++i) { tmpans_2 += x[i].x; } ans_2 = max(tmpans_1,tmpans_2); // cerr<<tmpans_1<<' '<<tmpans_2<<endl; return; } signed main() { #ifdef lky233 freopen("testdata.in","r",stdin); freopen("testdata.out","w",stdout); #endif n = poread(); m = poread(); for(register int i = 1; i <= n; ++i) datay[i].in(); for(register int i = 1; i <= m; ++i) datax[i].x = poread(); tan_1(); tan_2(); // cerr<<ans_1<<" "<<ans_2<<endl; cout<<max(ans_1,ans_2)<<endl; return 0; }
冗余代码极多还能优化……
三、意识到贪心是正确的然后来补充
Hack数据实际可认为是策略2。
策略2修正:破掉对方的盾牌,之后己方sigma Attack - 对方 sigma Attack;
策略1证明: 使用一个更小的数攻击对方的数,会减小贡献值。
策略2: 只要可以破掉对方盾牌,攻击顺序其实已经不会影响最终答案了……
代码咕咕咕掉,回宿舍了。