2018.10.23-dtoj-1608新三国争霸(fight)

题目描述:

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\n",f[t]);
  return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Jessie-/p/9838774.html