[SDOI2010] BZOJ 1922 大陆争霸-图论-最短路径-dijkstra算法

40 篇文章 0 订阅
13 篇文章 0 订阅

题目链接:右转进入题目

题目大意:给定一张有向带权图,但是到达一个点之前必须访问另一些点。

求从点1到点n的最短路径。

题解:

用一个“伪状态转移方程”来描述(之所以是说伪,是因为这是个图而不是个树,所以仅仅用来表示逻辑)

dist[x]=max{d[from[x]],min{dist[pre[x]]+e[i].wgt}}。

这个意思是:到达一个点的真正用时,是在到达它之前必须到达的点的用时的最大值,和所有到达它路径中最短的那一条的最小值取最大值。

说的好乱,最好画个图,例如原题中的图,以第六个点为例,

首先要保证3和5都被访问过了,因此取最大值;

同时要计算出访问4的真正时间再+wgt(4,6)。

然后这二者求个最大值就好了,我觉得到这为止应该就比较明白了。

然后好像由于代码写得太丑并没有用堆优化也过了(复杂度O(n^2+m))。

代码如下(注:d[x]表示访问存储x的结界发生器的城市的最大值,dist[x]则表示访问x的前驱结点的最小值,

辣么说答案就是max{d[n],dist[n]}辣!):

//SDOI 2010
//BZOJ 1922
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<climits>
#define MAXN 3010
#define MAXM 70010
#define INF LLONG_MAX
#define ull unsigned long long int
using namespace std;
struct edge{
	int from,to,wgt,pre;
}e[MAXM];
int h[MAXN],cnt[MAXN],etop,n,m;
ull dist[MAXN],d[MAXN];
vector<int> pro[MAXN];
bool vis[MAXN];
int add_edge(int u,int v,int w)
{
	etop++;
	e[etop].from=u;
	e[etop].to=v;
	e[etop].wgt=w;
	e[etop].pre=h[u];
	h[u]=etop;
	return 0;
}
int main()
{
	scanf("%d%d",&n,&m);
	etop=0;
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		if(u!=v) add_edge(u,v,w);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&cnt[i]);
		for(int j=1;j<=cnt[i];j++)
		{
			int pr;scanf("%d",&pr);
			pro[pr].push_back(i);
		}
	}
	for(int i=0;i<=n;i++)
		dist[i]=INF,d[i]=0;
	int s;dist[s=1]=0;
	memset(vis,false,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		int mink=0;
		for(int j=1;j<=n;j++)
			if(!vis[j]&&!cnt[j]&&max(dist[j],d[j])<max(dist[mink],d[mink]))
				mink=j;
		//if(mink==0) return 0;
		vis[mink]=true;
		for(int j=h[mink];j;j=e[j].pre)
			if(!vis[e[j].to]&&dist[e[j].to]>max(dist[mink],d[mink])+e[j].wgt)
				dist[e[j].to]=max(dist[mink],d[mink])+e[j].wgt;
		for(int j=pro[mink].size()-1;j>=0;j--)
		{
			cnt[pro[mink][j]]--;
			if(d[pro[mink][j]]<max(dist[mink],d[mink]))
				d[pro[mink][j]]=max(dist[mink],d[mink]);
		}
	}
	printf("%lld\n",max(dist[n],d[n]));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值