nyoj 247

题意:从1-n的一条路径中,找出两点,使得两点权值之差最大。n个点不一定要都经过。


解题思路:这道题实际可以转化为找出可达的两点a,b,使得a和b两点的权值之差最大。。。这道题确实很难想到是转化为最短路的模型,我开始还是按照论坛里面说的,先求强连通分量、缩点,最后再搜索,结果挂了。。

假设最后的结果是a-b,那么我们肯定要保证a>b,否则根据题意就无意义。并且我们还应该要有b->a,即可以从b走到a,那么就定义两个数组d_min[i],和d_max[i],分别表示从1->i经过点的最小值和从i->n经过点的最大值,最后只需找到最大的d_max[i] - d_min[i]即可。为了方便求d_max[i],我们应该要从n出发寻找,所以应该要建立反向边。

注意:这里是能够保证我们之前讲的两条性质,注意到d_min和d_max表示的意义,是1->i和i->n所经过的点的最小和最大值,如果a在b的前面,那么肯定d_max和d_min不会同时包含a和b的(画图即可明白),如果a<b,毫无疑问肯定算出的不会是最优值。

这里算d_min和d_max可以用spfa算法,确实spfa算法在最短路里面的应用非常广。总之这样运用spfa还是第一次见。。。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int maxn = 100005;
struct Edge
{
	int k,next;
}edge[maxn<<2];
int n,m,cnt,d_max[maxn],d_min[maxn];
int head1[maxn],head2[maxn];
bool s1[maxn],s2[maxn];

void addedge(int u,int v)
{
	edge[cnt].k = v;
	edge[cnt].next = head1[u];
	head1[u] = cnt++;

	edge[cnt].k = u;
	edge[cnt].next = head2[v];
	head2[v] = cnt++;
}

void spfa()
{
	queue<int> q;
	memset(s1,false,sizeof(s1));
	memset(s2,false,sizeof(s2));
	q.push(1);
	s1[1] = true;
	while(!q.empty())
	{
		int t = q.front();
		q.pop();
		for(int i = head1[t]; i != -1; i = edge[i].next)
		{
			int k = edge[i].k;
			d_min[k] = min(d_min[t],d_min[k]);
			if(s1[k] == false)
			{
				s1[k] = true;
				q.push(k);
			}
		}
	}
	q.push(n);
	s2[n] = true;
	while(!q.empty())
	{
		int t = q.front();
		q.pop();
		for(int i = head2[t]; i != -1; i = edge[i].next)
		{
			int k = edge[i].k;
			d_max[k] = max(d_max[t],d_max[k]);
			if(s2[k] == false)
			{
				s2[k] = true;
				q.push(k);
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; i++)
		if(s1[i] && s2[i])
		{
			ans = max(ans,d_max[i] - d_min[i]);
		}
	printf("%d\n",ans);
}

int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(head1,-1,sizeof(head1));
		memset(head2,-1,sizeof(head2));
		cnt = 0;
		for(int i = 1; i <= n; i++)
		{
			scanf("%d",&d_max[i]);
			d_min[i] = d_max[i];
		}
		for(int i = 1; i <= m; i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			addedge(x,y);
			if(z == 2)
				addedge(y,x);
		}
		spfa();
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值