枯法者训练(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<<'\n';
    return 0;
}
posted @ 2020-05-07 15:48  Suiyue_Li  阅读(243)  评论(0编辑  收藏  举报