枯法者训练(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;
}