DTOJ 1608 新三国争霸

1608: 新三国争霸(fight)

时间限制: 1 Sec  内存限制: 128 MB  
提交: 117  解决: 47

题目描述

PP 特别喜欢玩即时战略类游戏,但他觉得那些游戏都有美中不足的地方。灾害总不降临道路,而只降临城市,而且道路不能被占领,没有保护粮草的真实性。于是他就研发了《新三国争霸》。

在这款游戏中,加入灾害对道路的影响(也就是一旦道路W[i,j]受到了灾害的影响,那么在一定时间内,这条路将不能通过)和道路的占领权(对于一条道路W[i,j],至少需要K[i,j]个士兵才能守住)。当灾难发生时,不能在发生灾难的道路驻扎士兵。

PP可真是高手,不一会,就攻下了N-1座城市,加上原来的就有N座城市了,但他忽略了一点……那就是防守同样重要,不过现在还来的及。因为才打完仗所以很多城市都需要建设,PP估算了一下,大概需要T天。他现在无暇分身进攻了,只好在这T天内好好的搞建设了。所以他秒要派士兵占领一些道路,以确保任何两个城市之间都有路(不然敌人就要分而攻之了,是很危险的)。士兵可不是白干活的,每个士兵每天都要吃掉V的军粮。因为有灾害,所以方案可能有变化(每改变一次就需要K的军粮,初始方案也需要K的军粮)。

因为游戏是PP编的,所以他知道什么时候有灾害。PP可是一个很节约的人,他希望这T天在道路的防守上花最少的军粮。

N<=300,M<=5000 ,T<=50;

v<=20,p<=10000,c<=300

 

输入

第一行有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天在道路的防守上花费最少的军粮。

 

题解:

考虑DP。

f_{i}表示前i个时刻,所需要的最小花费。

那么,考虑转移:

f_{i}=min(f_{j}+get(j+1,i) +k ) (0 \leqs j<i)

表示你在j强制变化方案。

考虑这个get(l,r) ,我们可以用kruskal求出在(l,r)这段区间里均可以使用的边集中产生的最小生成树。

 

#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i++)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
typedef long long ll;
const int N=1e4+4,M=504;
int n,m,t,V,k,p;
struct EE{int u,v;ll w;}ee[N];
int e[M][M],tp,vis[N];ll f[N];
typedef pair<int,int>P;
vector<P>v[M][M];
bool cmp(EE a,EE b){return a.w<b.w;}
int fa[N];
#define mk(a,b) make_pair(a,b)
#define fi first 
#define se second
vector<P>::iterator it ;
in int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);}
in ll work(int l,int r){
	rep(i,1,m) vis[i]=0;
	rep(i,1,m){
		int x=ee[i].u,y=ee[i].v;
		if(v[x][y].empty()) continue;
		for(it=v[x][y].begin();it!=v[x][y].end();it++){
			P tmp=*it;
			int L=tmp.fi,R=tmp.se;
			if(L>R) swap(L,R);
			if((L>=l&&L<=r)||(R>=l&&R<=r)||(L<=l&&R>=r)){vis[i]=1;break;} 
		}
	}
	rep(i,1,n) fa[i]=i;
	int num=0;ll sum=0;
	rep(i,1,m){
		if(vis[i]) continue;
		int x=ee[i].u,y=ee[i].v;
		x=F(x),y=F(y);
		if(x!=y) fa[x]=y,sum+=ee[i].w,num++;
		if(num==n-1) break;
	}
	if(num==n-1) return sum;
	return 0;
}
int main(){
	g(n),g(m),g(t),g(V),g(k);
	rep(i,1,m) g(ee[i].u),g(ee[i].v),g(ee[i].w);
	sort(ee+1,ee+1+m,cmp);g(p);
	rep(i,1,p){
		int x,y,t1,t2;g(x),g(y),g(t1),g(t2);
		if(t1>t2) swap(t1,t2);
		v[x][y].push_back(mk(t1,t2));
		v[y][x].push_back(mk(t1,t2));
	}
	rep(i,1,t) f[i]=1e17;
	rep(i,1,t){
		For(j,0,i){
			int tmp=work(j+1,i);
			if(tmp) f[i]=min(f[i],f[j]+1ll*(i-j)*V*tmp+k);
		} 
	}	
	return !printf("%lld",f[t]);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱の小公举

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值