枯法者训练(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;
}
原文地址:https://www.cnblogs.com/Suiyue-Li/p/12843515.html