题目描述:
P 特别喜欢玩即时战略类游戏,但他觉得那些游戏都有美中不足的地方。灾害总不降临道路,而只降临城市,而且道路不能被占领,没有保护粮草的真实性。于是他就研发了《新三国争霸》。
在这款游戏中,加入灾害对道路的影响(也就是一旦道路W[i,j]受到了灾害的影响,那么在一定时间内,这条路将不能通过)和道路的占领权(对于一条道路W[i,j],至少需要K[i,j]个士兵才能守住)。当灾难发生时,不能在发生灾难的道路驻扎士兵。
PP可真是高手,不一会,就攻下了N-1座城市,加上原来的就有N座城市了,但他忽略了一点……那就是防守同样重要,不过现在还来的及。因为才打完仗所以很多城市都需要建设,PP估算了一下,大概需要T天。他现在无暇分身进攻了,只好在这T天内好好的搞建设了。所以他秒要派士兵占领一些道路,以确保任何两个城市之间都有路(不然敌人就要分而攻之了,是很危险的)。士兵可不是白干活的,每个士兵每天都要吃掉V的军粮。因为有灾害,所以方案可能有变化(每改变一次就需要K的军粮,初始方案也需要K的军粮)。
因为游戏是PP编的,所以他知道什么时候有灾害。PP可是一个很节约的人,他希望这T天在道路的防守上花最少的军粮。
输入:
第一行有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,T1,T2。表示X到Y的这条路,在T1到T2这几天都会受灾害。
输出:
T天在道路的防守上花费最少的军粮。
数据范围:
N<=300,M<=5000 ,T<=50;
v<=20,p<=10000,c<=300
算法标签:DP,Kruskal
思路:
这题数据范围都不大,考虑用f[i]前i天完成道路防守所需的最少军粮。转移考虑枚举一个j表示在i-j天里没有改变过方案,于是得到f[i]=max(f[j]+v*(j-i)*get(j+1,i)+k);其中get(x,y)表示在x-y天内用的道路的最小权值,我们可以去掉在这几天里不可用的道路,跑kruskal最小生成树。效率是O(m*n2)
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=305,M=5003;const LL inf=1e15;int n,m,t,v,k,s[N][N][53],fa[N];LL f[N];struct node{int l,r,c;}tt[M]; il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;} il int getfa(int x){if(fa[x]==x)return x;return fa[x]=getfa(fa[x]);} il bool cmp(node t1,node t2){return t1.c<t2.c;} il LL get(int a,int b){ for(int i=1;i<=n;i++)fa[i]=i;int num=0;LL res=0; for(int i=1;i<=m;i++){ if(s[tt[i].l][tt[i].r][b]-s[tt[i].l][tt[i].r][a-1]>0)continue; int x=getfa(tt[i].l),y=getfa(tt[i].r); if(x==y)continue; num++;res+=(LL)tt[i].c;if(num==n-1)break;fa[x]=y; } if(num==n-1)return res;return inf; } int main() { n=read();m=read();t=read();v=read();k=read(); for(int i=1;i<=m;i++)tt[i].l=read(),tt[i].r=read(),tt[i].c=read();sort(tt+1,tt+1+m,cmp); int P=read();while(P--){ int x=read(),y=read(),t1=read(),t2=read(); for(int i=t1;i<=t2;i++)s[x][y][i]++,s[y][x][i]++; } for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=t;k++)s[i][j][k]+=s[i][j][k-1]; for(int i=1;i<=t;i++)f[i]=inf; for(int i=1;i<=t;i++)for(int j=0;j<i;j++) f[i]=min(f[j]+(LL)(i-j)*(LL)v*get(j+1,i)+(LL)k,f[i]); printf("%lld ",f[t]); return 0; }