Codeforces Round #565 (Div. 3) F.Destroy it!

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 }

猜你喜欢

转载自www.cnblogs.com/SSummerZzz/p/11163488.html