zoukankan      html  css  js  c++  java
  • 枯法者训练(DP优化+最短路)

    问题 B: 枯法者训练

    时间限制: 1 Sec 内存限制: 128 MB

    题目描述

    你的⽇常活动之⼀是训练你的枯法者⼤军。你⼿下有N个枯法者,第i个枯法者的攻击⼒是⼀个恒定值Ai,但由于它们智⼒不同,有的枯法者可以像嚼了X迈⼀样根本停不下来放倒boss,有的枯法者只能站桩。
    现在,你又⼀次把你的枯法者⼤军带到boss⾯前,毫不意外地wipe(团灭)。
    你开始怀疑是不是暴雪公司乱改平衡性,偷偷削弱了你的枯法者,于是你打开了战⽃记录,发现你的枯法者在wipe前⼀共对boss造成了X点伤害。
    你想知道有多少种伤害值是你的枯法者打不出的,以及这些伤害值中最⼤的是多少。假设boss有⾜够多的⾎量抗住这些伤害。

    输入

    第⼀⾏⼀个整数N,表⽰你的枯法者个数。
    第⼆⾏N个整数Ai,表⽰第i个枯法者的攻击⼒。

    输出

    两个数,第⼀个数表⽰有多少种打不出的伤害值,第⼆个数表⽰最⼤的打不出的伤害值是多少。
    保证答案有限。
    注意:0点伤害永远是打得出的,因为你可能就是⾮洲⼈⽽已。

    样例输入

    2
    3 5

    样例输出

    4 7

    提示

    100%的数据满足:(A_i≤10^6,N≤10)

    做法:

    这个题的做法同牛场围栏(fence)一样,具体的找不到链接了。
    下面讲讲做法:
    一个很自然的想法是通过动态规划计算出对于每个伤害值(X),能否通过现有的攻击力打出,但是(X)的大小是没有限制的,即状态的总数可以达到无限,必须减少状态总数。

    如果存在攻击力为(L)的枯法者,注意到如果可以打出(x)的伤害,那么(x+L,x+2L,x+3L,...,x+kL...)的伤害都可以被打出。

    我们将状态设为(f(i),(0<=i<L))表示最小的整数(x),满足(x)可以被打出,且(x\%L=i),如果(x)不存在,则(f(i))为正无穷

    为了减少状态总数,加快速度,我们取(L=min{A_1,A_2,...,A_m})

    如果我们能计算出所有的(f(i)),则答案(ans=max{f(i)-L})(ans<0) 时则所有的伤害值都可以被打出

    状态转移方程:
    (f(i)=min{f(j)+A_k} ,((j+A_k) \% L=i))

    那么如何划分阶段?

    如果按照(i)从小到大划分阶段,不满足无后效性。

    仔细观察方程,注意到(f(0)=0),我们建立一个以 (0) 为原点的 (N) 个结点的图,如果 ((j+l_k)\%L=i) ,相当于从 (i)(j) 有一条权值为 (l_k) 的边。那么 (f(i)) 就是从 (0)(i) 的最短路。

    时间复杂度(O(NL))

    //#pragma GCC optimize(2)
    //#pragma GCC optimize(3, "Ofast", "inline")
    #include "bits/stdc++.h"
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<ll,ll> pii;
    const int N=1e6+5;
    const ll mod=1e9+7;
    const double eps=1e-5;
    //const double pi=acos(-1);
    struct node
    {
        int v,w;
    };
    vector<node>g[N];
    int a[N],vis[N];
    ll d[N];
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        int p=a[1];
        for(int i=0;i<p;i++)
        {
            for(int j=2;j<=n;j++)
            {
                g[i].emplace_back(node{(i+a[j])%p,a[j]});
            }
        }
        memset(d,0x3f,sizeof d);
        d[0]=0;
        queue<int>q;
        q.push(0);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();vis[u]=0;
            for(auto t:g[u])
            {
                if(d[t.v]>d[u]+t.w)
                {
                    d[t.v]=d[u]+t.w;
                    if(!vis[t.v])
                        q.push(t.v),vis[t.v]=1;
                }
            }
        }
        ll ans=0,cnt=0;
        for(int i=0;i<p;i++)
        {
            if(d[i]>i)
            {
                cnt+=(d[i]-i)/p;
                ans=max(ans,d[i]-p);
            }
        }
        cout<<cnt<<' '<<ans<<'
    ';
        return 0;
    }
    
  • 相关阅读:
    OSEK简介
    线性代数-矩阵-【5】矩阵化简 C和C++实现
    线性代数-矩阵-转置 C和C++的实现
    线性代数-矩阵-【3】矩阵加减 C和C++实现
    线性代数-矩阵-【1】矩阵汇总 C和C++的实现
    线性代数-矩阵-【4】点乘 C和C++的实现
    线性代数-矩阵-【2】矩阵生成 C和C++实现
    数据结构-环形队列 C和C++的实现
    数据结构-二叉树 C和C++实现
    数据结构-单向链表 C和C++的实现
  • 原文地址:https://www.cnblogs.com/Suiyue-Li/p/12843515.html
Copyright © 2011-2022 走看看