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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章