新三国争霸( 最小生成树dp\(\star \star \))
- 时限:\(1s\) 内存:\(256M\)
Descrption
- \(PP\)
- 在这款游戏中,加入灾害对道路的影响(也就是一旦道路 \(W_{i,j}\) 受到了灾害的影响,那么在一定时间内,这条路将不能通过)和道路的占领权(对于一条道路 \(W_{i,j}\),至少需要\(K_{i,j}\)
- PP可真是高手,不一会,就攻下了 \(N-1\) 座城市,加上原来的就有 \(N\) 座城市了,但他忽略了一点……那就是防守同样重要,不过现在还来的及。因为才打完仗,所以很多城市都需要建设,\(PP\) 估算了一下,大概需要 \(T\) 天。他现在无暇分身进攻了,只好在这 \(T\)
- 士兵可不是白干活的,每个士兵每天都要吃掉 \(V\) 的军粮。因为有灾害,所以方案可能有变化(每改变一次就需要 \(K\) 的军粮,初始方案也需要 \(K\)
- 因为游戏是 \(PP\) 编的,所以他知道什么时候有灾害。\(PP\) 可是一个很节约的人,他希望这 \(T\)
Input
- 第一行有 \(5\) 个整数 \(N,M,T,V,K\)。\(N\) 表示有城市数,\(M\) 表示道路数,\(T\) 表示需要修养的天数,\(V\) 表示每个士兵每天吃掉的军粮数,\(K\)
- 以下 \(M\) 行,每行 \(3\) 个数 \(A,B,C\)。表示 \(A\) 与 \(B\) 有一条路(路是双向的)需要 \(C\)
- 第 \(M+2\) 行是一个数 \(P\),表示有 \(P\)
- 以下 \(P\) 行,每行 \(4\) 个数,\(X,Y,T_1,T_2\)。表示 \(X\) 到 \(Y\) 的这条路,在 \(T_1\) 到 \(T_2\)
Output
- \(T\)
Sample Input
3 3 5 10 30
1 2 1
2 3 2
1 3 4
1
1 3 2 5
Sample Output
180
Hint
- \(N<=300,M<=5000,T<=50\),保证结果不超过 \(2^{31}-1\)
- 来源:
分析
- 如果没有灾难,此问题就是一个再简单不过的最小生成树问题,那加上灾难这个变量那造成的后果是有可能每一天的最小生成树不一样,生成树不一样那每天士兵的人数也不一样。如果我们能直到每一天的需要的士兵人数。那就相当于给一个 \(1\sim T\) 的序列,序列元素是你每天安排的士兵的最少人数,每一天你选择当天的最小值,也可以选择沿用上一天的士兵人数(如果可行的话),但是如果今天的士兵人数和昨天的不一样时需要额外的代价 \(k\)。
- 模型分析到这里,我们就能很容想到用 \(dp\) 来处理。定义 \(dp[i]\) 表示第 \(i\) 天的最小代价,转移方程有:
- \(dp[i]=min(dp[i],d[j]+w[j+1][i]*V*(i-j)+K),(0\le j<i)\)
- \(w[j+1][i]\) 表示 \(j+1\sim i\)
- 显然现在问题如何知道 \(w[j+1][i]\) ,\(n\le 300\) ,范围比较小,我们可以 \(n^2\)
- 首先向 \(lin4xu\) 大佬说声 \(sorry\)
Code
#include<cstdio>
#include<algorithm>
const int maxn=300+5,maxm=5000+5,Inf=0x7fffffff;
using namespace std;
struct Node{
int x,y,v;
}e[maxm];
bool cmp(Node a,Node b){return a.v<b.v;}
bool cr[51][maxn][maxn];
int n,m,t,v,k,p,father[301],w[51][51],f[51];
int find(int x){return x==father[x]?x:father[x]=find(father[x]);}
bool Check(int t1,int t2,int u,int v){//判断从t1到t2天边(u,v)是否有灾
for(int i=t1;i<=t2;i++)
if(cr[i][u][v])return 0;
return 1;
}
int krus(int a,int b){//从a天到b天,除去受灾的道路的最小生成树
int t=0;
for(int i=1;i<=n;i++)father[i]=i;
for(int i=1;i<=m;i++){
int x=e[i].x,y=e[i].y;
if(Check(a,b,x,y)&&find(x)!=find(y)){
father[find(x)]=find(y);
t+=e[i].v;
}
}
for(int i=1;i<=n;i++)//判断图是否连通
if(find(i)!=find(1))
return Inf;
return t;
}
void dp(){
for(int i=1;i<=t;i++){
f[i]=v*w[1][i]*i+k;
for(int j=1;j<=i;j++)
if(w[j+1][i]!=Inf)
f[i]=min(f[i],f[j]+k+w[j+1][i]*v*(i-j));
}
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&t,&v,&k);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
}
sort(e+1,e+m+1,cmp);
scanf("%d",&p);
for(int i=1;i<=p;i++){
int x,y,t1,t2;
scanf("%d%d%d%d",&x,&y,&t1,&t2);
if(x>y)swap(x,y);
if(t1>t2)swap(t1,t2);
for(int j=t1;j<=t2;j++)cr[j][x][y]=1;
}
for(int i=1;i<=t;i++)
for(int j=i;j<=t;j++)
w[i][j]=krus(i,j);
dp();
printf("%d",f[t]);
return 0;
}
hzoi