P6037 Ryoku 的探索&P2661 信息传递(拓扑排序求环)

P6037 Ryoku 的探索
题目背景
Ryoku 对自己所处的世界充满了好奇,她希望能够在她「死」之前尽可能能多地探索世界。

这一天,Ryoku 得到了一张这个世界的地图,她十分高兴。然而,Ryoku 并不知道自己所处的位置到底在哪里,她也不知道她会什么时候死去。她想要知道如何才能尽可能多的探索这个世界。
题目描述
Ryoku 所处的世界可以抽象成一个有 nn 个点, nn 条边的带权无向连通图 GG。每条边有美观度和长度。

Ryoku 会使用这样一个策略探索世界:在每个点寻找一个端点她未走过的边中美观度最高的走,如果没有边走,就沿着她前往这个点的边返回,类似于图的深度优先遍历。

探索的一个方案的长度是这个方案所经过的所有边长度的和(返回时经过的长度不用计算)。

她想知道,对于每一个起点 s=1,2,\cdots,ns=1,2,⋯,n,她需要走过的长度是多少?

输入格式
输入包含 n + 1n+1 行,其中第一行包含一个整数 nn。
接下来 nn 行每行包含四个整数 u,v,w,pu,v,w,p,描述了一条连接 uu 和 vv,长度为 ww,美观度为 pp 的无向边。

输出格式
输出包含 nn 行,每行一个整数,第 ii 行为 s=is=i 时的答案。

输入输出样例
输入 #1复制

5
4 1 2 1
1 2 3 2
3 1 1 4
3 5 2 5
2 3 2 3

输出 #1复制

7
7
8
7
8

说明/提示
【样例 1 说明】

以下为输入输出样例 1 中的图: (边上红色数组为 pp,黑色为 ww)

在这里插入图片描述

若起点为 11,顺序为 1\to3\to5\to2\to41→3→5→2→4,长度之和为 77。
若起点为 22,顺序为 2\to3\to5\to1\to42→3→5→1→4,长度之和为 77。
若起点为 33,顺序为 3\to5\to1\to2\to43→5→1→2→4,长度之和为 88。
若起点为 44,顺序为 4\to1\to3\to5\to24→1→3→5→2,长度之和为 77。
若起点为 55,顺序为 5\to3\to1\to2\to45→3→1→2→4,长度之和为 88。

【数据规模与约定】

对于 40%40% 的数据,n\le 10^3n≤10 3 。
对于 100%100% 的数据,3 \le n \le 10^63≤n≤10 6
,1 \le u,v,p \le n1≤u,v,p≤n,0\le w\le 10^90≤w≤10 9 ,保证 pp 互不相同。

第一次写这个类型的题,开始dfs写只有40分,后来寻思着是不是最小生成树,想着想着自闭了,今天看题解是拓扑排序,想想好长时间没写图了,该练练了。

题目给了n个点n条边,这样的图一定存在一个环,这样的图称为基环树。要遍历基环树时需走n-1条边,有个边是不用走的,求答案时用总长度减去那条边即可。怎么求那条边呢?进入基环树中的环后,会从这个点沿着美度高的点向前遍历,所以最后与这个点相邻的环中的美度最低点就是要抛去的(样例图中模拟一遍就会发现,如从3进入,3->1->2,3与2相邻的边要抛弃)。
实现步骤:
1.拓扑排序求出基环树的环;
2.依次求出环中的点x需要抛弃的边;
3.与x可到达的非环中的点也要减去抛弃的边。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define maxn 2000010
const int inf=0x3f3f3f3f;
struct node{
	int next,to,w,p;
}s[maxn];
int du[maxn],num=0,head[maxn],vis[maxn],ans[maxn];
void add(int x,int y,int w,int p){
	s[++num].next=head[x];
	s[num].to=y;
	s[num].w=w;
	s[num].p=p;
	head[x]=num;
}
void dfs(int x,int w){
	ans[x]=w;
	for(int i=head[x];i;i=s[i].next){
		if(vis[s[i].to]&&!ans[s[i].to]){//x可到达非环的点 
			dfs(s[i].to,w);
		}
	}
}
int main()
{
	int n,i,j,m,x,y,w,p;
	long long sum=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d %d %d %d",&x,&y,&w,&p);
		add(x,y,w,p);
		add(y,x,w,p);
		du[x]++;du[y]++;
		sum+=w;
	}
	queue<int>q;
	for(i=1;i<=n;i++)//入读为一的点进入队列(边界点) 
		if(du[i]==1) q.push(i);
	while(!q.empty()){//拓扑排序求环
		int t=q.front();
		q.pop();
		vis[t]=1;//标记的非环中的点 
		for(i=head[t];i;i=s[i].next){
			int u=s[i].to;
			du[u]--;
			if(du[u]==1) q.push(u);//环与边界点之间的点 
		}
	}
	for(i=1;i<=n;i++){
		if(!vis[i]){
			int mw,ms=inf;
			for(j=head[i];j;j=s[j].next){//求与这个点相邻的环中的美度最低点
				if(!vis[s[j].to]&&s[j].p<ms){
					ms=s[j].p;
					mw=s[j].w;
				}
			}
			dfs(i,mw);
		} 
	}
	for(i=1;i<=n;i++)
		printf("%lld\n",sum-ans[i]);
	return 0;
}

P2661 信息传递

拓扑排序求最小环

#include<cstdio>
#include<queue>
using namespace std;
const int maxn=400010;
struct node{
	int to,next;
}s[maxn];
int num,head[maxn],du[maxn],vis[maxn];
void add(int u,int v){
	s[++num].next=head[u];
	s[num].to=v;
	head[u]=num;
}
int dfs(int x){
	vis[x]=1;
	for(int i=head[x];i;i=s[i].next){
		if(!vis[s[i].to]){
			return dfs(s[i].to)+1;
		}
	}
	return 1;
}
int main()
{
	int x,n,i,j,m;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		add(i,x);
		du[x]++;
	}
	queue<int>q;
	for(i=1;i<=n;i++)
		if(!du[i]) q.push(i);
	while(!q.empty()){
		int t=q.front();
		q.pop();
		vis[t]=1;
		for(i=head[t];i;i=s[i].next){
			int u=s[i].to;
			du[u]--;;
			if(!du[u]) q.push(u);
		}
	}
	int ans=0x3f3f3f3f;
	for(i=1;i<=n;i++){
		if(!vis[i]){//求环的长度 
			ans=min(ans,dfs(i));
		}
	}
	printf("%d",ans);
	return 0;
}

并查集求最小环

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int inf=0x3f3f3f3f;
int a[200005],cont;
int find(int x){
	cont++;
	return x==a[x]?x:(find(a[x]));
}
int main()
{
	int m,i,j,n,x,y,k,ans=inf;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		a[i]=i;
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		cont=0;
		if(find(x)==i)
			ans=min(cont,ans);
		else 
			a[i]=x;
	}
	printf("%d",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章