【bzoj1003】[ZJOI2006]物流运输 最短路+dp

传送门:嘿原题在这

题意:给出一个图,求1~m的最短路,但其中有些点在某些时间段会不能用,题目保证有解。

我们可以很(不)容易的看出递推关系式dp[i]=min(dp[i],dp[j]+cost[j+1][i]+k)(0<=j<i)cost[i][j]表示从i到j这段时间的最短路。

//bzoj1003[ZJOI2006]物流运输
//by dadatu
#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
#define lim 1010
using namespace std;

queue<int> q;
int v[lim],head[lim],nextt[lim],c[lim],visit[lim],dist[lim],tag[lim],dp[lim],f[lim][lim],cost[lim][lim];
int n,m,e,k,d,tot=0;

void add_edge(int x,int y,int z){v[++tot]=y;c[tot]=z;nextt[tot]=head[x];head[x]=tot;}
void spfa()
{
	memset(dist,inf,sizeof(dist));memset(visit,0,sizeof(visit));
	q.push(1);visit[1]=1;dist[1]=0;
	while(!q.empty())
	{
		int x=q.front();q.pop();visit[x]=0;
		for (int i=head[x];i;i=nextt[i])
		{
			int y=v[i];
			if (!tag[y]&&dist[y]>dist[x]+c[i])
			{
				 dist[y]=dist[x]+c[i];
				 if (!visit[y]) q.push(y),visit[y]=1;
			}
		}

	}
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&k,&e);
	for (int i=1;i<=e;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add_edge(x,y,z);add_edge(y,x,z);
	}
	scanf("%d",&d);
	for (int i=1;i<=d;i++)
	{
		int p,l,r;
		scanf("%d%d%d",&p,&l,&r);
		for (int j=l;j<=r;j++) f[p][j]=1;//记录一下l至r这段时间p码头被封
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=i;j<=n;j++)
		{
			memset(tag,0,sizeof(tag));
			for (int x=1;x<=m;x++)
				for (int y=i;y<=j;y++) tag[x]|=f[x][y];//被封就打标记
			spfa();
			if (dist[m]<inf) cost[i][j]=dist[m]*(j-i+1);
			else cost[i][j]=inf;//如果这种情况不存在cost也要赋值
		}

	}
	memset(dp,inf,sizeof(dp));
	dp[0]=-k;//如果一路不改路线就不用加上k了
	for (int i=1;i<=n;i++)
		for (int j=0;j<i;j++) dp[i]=min(dp[i],dp[j]+cost[j+1][i]+k);//递推式
	printf("%d\n",dp[n]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值