F. Destroy it!
题目地址:http://codeforces.com/contest/1176/problem/F
思路:其实就是一个01背包问题,只是添加了回合和每回合的01限制,和每当已用牌数到了10的倍数,那张卡会触发double攻击。
因为卡使用的多少会触发double效果,所以我们要记录攻击的同时记录卡的使用次数,可以由01背包dp[N][N]改变,
第一个维度是当前是第几个回合,第二个维度是记录卡在用了n张的情况下造成的攻击力,但是dp[N][N](N <= 2e5),
占用内存太大显然不行。于是想到用dp[N][10]。
(因为卡的数量可能很多,我们其实只要记录最多三张1费,一张2费,一张3费)
每个回合用卡情况最多3种:
1.用3张一费卡。
2.用2张一费卡,或者1张一费卡,1张二费卡
3.用1张一费卡,或者1张二费卡,或者1张三费卡
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 #define rep(i,j,k) for(int i = (j); i <= (k); i++) 5 #define per(i,j,k) for(int i = (j); i >= (k); i--) 6 #define mod(x) ((x)%(MOD)) 7 8 typedef long long LL; 9 const int MOD = 10; 10 const int N = 2e5 + 10; 11 LL dp[N + 2][10]; 12 13 void init(){ 14 rep(i, 0, N) rep(j, 0, 9) dp[i][j] = -1; 15 } 16 17 int main(){ 18 19 ios::sync_with_stdio(false); 20 cin.tie(0); 21 22 init(); 23 24 int n; 25 cin >> n; 26 27 dp[0][0] = 0;//初始化 28 29 rep(o, 1, n){ 30 int num; 31 cin >> num; 32 33 int L1 = 0; 34 int L2 = 0; 35 int L3 = 0; 36 int c1[5] = { 0 };//费用1,记录v最大的三张 37 int c2 = 0;//费用2 38 int c3 = 0;//费用3 39 40 int c, v; 41 rep(i, 1, num){ 42 cin >> c >> v; 43 44 if (c == 1){ 45 if (L1 == 3){ 46 int x = 1; 47 if (c1[x] > c1[2]) x = 2; 48 if (c1[x] > c1[3]) x = 3; 49 if (v > c1[x]) c1[x] = v; 50 } 51 else c1[++L1] = v; 52 } 53 else if (c == 2){ 54 L2 = 1; 55 if (v > c2) c2 = v; 56 } 57 else if (c == 3){ 58 L3 = 1; 59 if (v > c3) c3 = v; 60 } 61 } 62 63 sort(c1 + 1, c1 + 1 + 3); 64 65 int max_v = max(c1[3], max(c2, c3));//一张v最大的 66 int _2_1 = c1[3];//两张卡费用最大的 67 int _2_2 = c1[2]; 68 if (c2 > _2_2) _2_2 = c2; 69 if (_2_2 > _2_1) swap(_2_2, _2_1);//两张卡,由1费中间v最大的2张,和一张2费的中间选出v最大的两张 70 LL _3 = c1[1] + c1[2] + c1[3];//用三张1费 71 72 rep(i, 0, 9) dp[o][i] = dp[o - 1][i];//先把上个回合的状态保存到当前回合,方便下边的比较 73 //因为这三种状态都是附加在上个状态下得出的,所以他们之间都是独立的。 74 //mod(i + x),(i + x >= 10)* value 用于判断是否满足大于等于用的次数是十张的倍数 75 rep(i, 0, 9){ 76 if (dp[o - 1][i] != -1){//上个状态是存在的 77 if (L1 + L2 + L3 >= 1){//用的卡牌最少一张的时候,用一张情况 78 79 dp[o][mod(i + 1)] = max(dp[o][mod(i + 1)], 80 dp[o - 1][i] + max_v + (i + 1 >= 10)*max_v); 81 82 } 83 if (L2 + L1 >= 2){//1费2费用的卡牌至少有两张,用两张情况 84 85 dp[o][mod(i + 2)] = max(dp[o][mod(i + 2)], 86 dp[o - 1][i] + _2_1 + _2_2 + (i + 2 >= 10)*_2_1); 87 88 } 89 if (L1 >= 3){//1费卡牌数最少三张,用三张情况 90 91 dp[o][mod(i + 3)] = max(dp[o][mod(i + 3)], 92 dp[o - 1][i] + _3 + (i + 3 >= 10)*c1[3]); 93 94 } 95 } 96 } 97 } 98 99 LL ans = -1; 100 101 //rep(i, 0, n){ 102 // rep(o, 0, 9) cout << dp[i][o] << " "; 103 // cout << endl; 104 //} 105 //cout << endl; 106 107 rep(o, 0, 9) ans = max(dp[n][o], ans); 108 109 cout << ans << endl; 110 111 return 0; 112 }