洛谷 P1262 間諜網絡 tarjan縮點

題目描述
由於外國間諜的大量滲入,國家安全正處於高度的危機之中。如果A間諜手中掌握着關於B間諜的犯罪證據,則稱A可以揭發B。有些間諜收受賄賂,只要給他們一定數量的美元,他們就願意交出手中掌握的全部情報。所以,如果我們能夠收買一些間諜的話,我們就可能控制間諜網中的每一分子。因爲一旦我們逮捕了一個間諜,他手中掌握的情報都將歸我們所有,這樣就有可能逮捕新的間諜,掌握新的情報。

我們的反間諜機關提供了一份資料,包括所有已知的受賄的間諜,以及他們願意收受的具體數額。同時我們還知道哪些間諜手中具體掌握了哪些間諜的資料。假設總共有n個間諜(n不超過3000),每個間諜分別用1到3000的整數來標識。

請根據這份資料,判斷我們是否有可能控制全部的間諜,如果可以,求出我們所需要支付的最少資金。否則,輸出不能被控制的一個間諜。

輸入格式
第一行只有一個整數n。

第二行是整數p。表示願意被收買的人數,1≤p≤n。

接下來的p行,每行有兩個整數,第一個數是一個願意被收買的間諜的編號,第二個數表示他將會被收買的數額。這個數額不超過20000。

緊跟着一行只有一個整數r,1≤r≤8000。然後r行,每行兩個正整數,表示數對(A, B),A間諜掌握B間諜的證據。

輸出格式
如果可以控制所有間諜,第一行輸出YES,並在第二行輸出所需要支付的賄金最小值。否則輸出NO,並在第二行輸出不能控制的間諜中,編號最小的間諜編號。

輸入輸出樣例
輸入 #1
3
2
1 10
2 100
2
1 3
2 3
輸出 #1
YES
110
輸入 #2
4
2
1 100
4 200
2
1 2
3 4
輸出 #2
NO
3

解法:tarjan縮點

  • 對於任何一個環,我們都可以去中間的最小值,因爲只要收買一個,剩下的就都可以控制

  • 我們建完圖之後直接縮點即可,如何判斷不可以呢,如果一個點的dfn追溯值爲0,那我們直接輸出該點即可

  • 對於任何一個人,我們枚舉和他相連的所有邊,如果中間的編號不一樣,即不是在一個環上,那我們就可以就可以把連接的那個人的入度加一,在後面累加答案時,我們只需要加上所有入度爲0的點即可,因爲沒有點可以到他,只能收買

AC代碼

#include<cstdio>
#include<stack>
#include<cstring>
#define inf 0x3f3f3f3f
#define re register int
using namespace std;
struct edge {
	int nex,to;
}e[8080];
int n,p,r,ans,cnt,m[3030],head[3030],sum[3030];
int num,tot,dfn[3030],low[3030],a[3030],ru[3030];
bool v[3030]; stack<int> s;
inline int read() {
	int x=0,cf=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-') cf=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*cf;
}
inline void add(int x,int y) {
	e[++cnt].to=y,e[cnt].nex=head[x],head[x]=cnt;
}
inline int min(int A,int B) { return A<B?A:B; }
inline void tarjan(int x) {
	dfn[x]=low[x]=++num;
	s.push(x),v[x]=true;
	for(re i=head[x];i;i=e[i].nex) {
		int y=e[i].to;
		if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
		else if(v[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]) {
		tot++;
		while(x!=s.top()) {
			a[s.top()]=tot;
			sum[tot]=min(sum[tot],m[s.top()]);
			v[s.top()]=false; s.pop();
		}
		a[s.top()]=tot;
		sum[tot]=min(sum[tot],m[s.top()]);
		v[x]=false; s.pop();
	}
	return;
}
int main() {
	n=read(),p=read();
	memset(m,0x3f,sizeof(m));
	memset(sum,0x3f,sizeof(sum));
	for(re i=1;i<=p;i++) {
		int x=read();
		m[x]=read();
	}
	r=read();
	for(re i=1;i<=r;i++) {
		int x=read(),y=read();
		add(x,y);
	}
	for(re i=1;i<=n;i++) {
		if(!dfn[i]&&m[i]!=inf) tarjan(i);
	}
	for(re i=1;i<=n;i++) {
		if(!dfn[i]) {
			printf("NO\n%d",i);
			return 0;
		}
	}
	for(re i=1;i<=n;i++) {
		for(re j=head[i];j;j=e[j].nex) {
			if(a[i]!=a[e[j].to]) ru[a[e[j].to]]++;
		}
	}
	for(re i=1;i<=tot;i++) {
		if(!ru[i]) ans+=sum[i];
	}
	printf("YES\n%d",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章