Codeforces Round #586 (Div. 1 + Div. 2) E. Tourism(拓扑排序)

题目链接

https://codeforces.com/contest/1220/problem/E

题意

一个人在原点s,每个城市有对应的评价值w,求他在整个图中经过的城市累积的评价值的最大值

思路

其实只用考虑两种情况,从叶子开始的一条链与图中的环这两种情况,
在这里插入图片描述
以上面这个图为例,起点是6,我们假设从1到6每个节点的价值分别为1,2,3,4,5,6
那么问题要求的就是从6出发最后能累积多少价值
答案是21
路线为:6-2-1-3-4-2-6-5
注意题目中有一个要求:

Alex believes that his trip will be interesting only if he will not use any road twice in a row. That is if Alex came to city v from city u, he may choose as the next city in the trip any city connected with v by the road, except for the city u.

这句话翻译过来就是不能走上次刚走的点,比如最开始从6-2,下一次不能立即从2-6,但可以通过绕一圈6-2-1-3-4-2-6的方式回到6,意味着每个节点每条边都有可能经过多次,每个节点只算第一次经过时候的值。
方法:
1.利用拓扑排序,每次从叶子往上走,将叶子所在的一条链上的值累加到环的节点(如果链上遇到初始点则这条链累积到初始点)
2.环上的节点一定可以保证相互之间可以到达
最后的结果就是环上的价值和+max(链上累积的价值和),因为最多只能去某一条链
//拓扑排序时注意对孤立点的处理

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
//ans可能爆long long
const int N=200010;
int deg[N];
ll w[N],cnt[N]; 
int v[N];
int s,n,m;
queue<int> Q;
vector<int> ed[N];
//对于单点的考虑 
int main(){
//	freopen("p.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<=m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		ed[a].push_back(b);
		ed[b].push_back(a);
		deg[a]++;//出度
		deg[b]++;//出度 
	}
	scanf("%d",&s);
	
	for(int i=1;i<=n;i++) if(deg[i]==1 && i!=s) Q.push(i);//叶子
 
	while(!Q.empty()){
		int now=Q.front();Q.pop();
		deg[now]=-1;//不能deg[now]--,对于孤立点的考虑 
		
		for(auto nxt:ed[now]){
			if(deg[nxt]<0) continue;//1->2
			
			deg[nxt]--;
			cnt[nxt]=max(cnt[nxt],cnt[now]+w[now]);
			
			if(deg[nxt]==1 && nxt!=s) Q.push(nxt);
		}
	} 
	
	ll sum=0,sum1=0;
	for(int i=1;i<=n;i++){
		if(deg[i]<0) continue;//不能等于0,如果是孤立点,将少考虑一种情况 
		sum+=w[i];
		sum1=max(sum1,cnt[i]);
	}
	
	ll ans=sum+sum1;
	cout<<ans<<"\n";
	return 0;
}
//样例 
//1 0
//1000000000
//1
posted @ 2019-09-22 14:50  xzhws  阅读(34)  评论(0编辑  收藏  举报