TC SRM div2 500

打小兵

题目描述:

一共有20个人,每个人的hp~( 1,60 ) 每次进行一次攻击,造成9 3 1的攻击,对三个人。问最少攻击几次可以把所有的人都干掉。

题解:

20个人,每个人60种可能,不能表示完。总感觉有贪心策略,但是边界条件搞不掉。于是答案竟然还是dp暴力。只是dp【a】【b】【c】表示用几个9 3 1,对于每一个数,枚举情况看它怎么拆分,是一个数一个数的去考虑,而不是一次931一次931的去用。注意很重点的是,一个数拆分的总个数要少于总的使用次数,这样一定可以把他们分在不同的组里面。可以判定m次来写,也可以直接dp

重点:

关键是思维的变化。不再是一次一次的去用,而是一个数一个数的拆分干掉。然后为了满足题意,再添加限定条件。之后由于9 3 1只有三个数,因此描述状态就好描述了。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 101;
const int INF = 1e6 +10;
int dp[21][maxn][maxn][maxn];

int x[maxn], n;

class MutaliskEasy//没有用判定,其实判定思路更好写
{//我是用的dp的值表示的到达此状态限定的至少得次数。
public:
    int minimalAttacks(vector<int> t)
    {
        n = t.size();
        REP(i, 0, n)
        {
            x[i + 1] = t[i];
        }
        REP_D(i, 0, 100)//初始化,不能达到此状态
        {
            REP_D(j, 0, 100)
            {
                REP_D(k, 0, 100)
                {
                    REP_D(z, 0, n)
                    {
                        dp[z][i][j][k] = INF;
                    }
                }
            }
        }
        dp[0][0][0][0] = 0;//解决0个用了0个931
        REP_D(i, 1, n)//更新的dp
        {
            for(int a = 0; a <= 100; a++)
            {
                for(int b = 0; b <= 100; b++)
                {
                    for(int c = 0; c <= 100; c++)
                    {
                        if(dp[i-1][a][b][c]==INF)//abc是合法的时候
                            continue;//下面拆分数字,注意,枚举9几个3几个,注意都要多枚举一次,因为9的时候可能搞的8
                        for(int aa = 0; aa*9 <= x[i] + 9; aa++)
                        {
                            for(int bb = 0; aa*9+bb*3 <= x[i] + 12; bb++)
                            {
                                int cc = x[i]-aa*9-bb*3;
                                cc = max(0, cc);
                                if(a+aa<=100&&b+bb<=100&&c+cc<=100)
                                {//转移方程
                                    dp[i][a+aa][b+bb][c+cc]=min(dp[i][a+aa][b+bb][c+cc], max(aa+bb+cc,dp[i-1][a][b][c]));
                                }
                            }
                        }
                    }
                }
            }
        }
        int ans = INF;//把n个都干掉,并且再算上限定次数
        REP_D(a, 0, 100)
        {
            REP_D(b, 0, 100)
            {
                REP_D(c, 0, 100)
                {
                    int tmp = max(a, b);
                    tmp = max(tmp, c);
                    tmp = max(tmp, dp[n][a][b][c]);
                    ans = min(ans, tmp);
                }
            }
        }
        return ans;
    };
};

int main()
{
    freopen("13Min.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    int tt;
    vector<int> y;
    while(scanf("%d", &tt) != EOF)
    {
        y.push_back(tt);
    }

    MutaliskEasy a;
    cout << a.minimalAttacks(y) << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值