【习题讲解】 图论类习题

匿名 (未验证) 提交于 2019-12-02 22:56:40
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/83817767

重量不同的硬币

题目描述
Fj有N个硬币,编号为1…N。
现在有W个推断,为(A,B),表示硬币A比硬币B重。
寻找并输出一个硬币编号,要求其重量明确不同于其他硬币的个数最多。
如果有多个答案,输出字典序最小的一个。
如果给出的数据有矛盾,输出"IMPOSSIBLE"
输入格式
Line 1: 两个整数: N and W.
Lines 2…W+1: 每行两个整数: A, B
输出格式
Line 1: 重量不同于其他硬币的个数最多的硬币编号。
样例数据
input

7 6
1 6
1 5
3 6
4 3
2 4
2 5
output
2

首先,我们知道,如果重量的关系出现了矛盾,一定出现了环,我们可以选择用拓扑排序进行判环:
拓扑排序中的点,要想进入拓扑序列,入度必然为0;但是有环,入度必然不为0.因此,我们统计拓扑排序有多少个点进入拓扑序列,如果有点数sum<n,那么就输出Inpossible。
对于统计大小,我们进行dfs遍历,用cnt[]表示连向这个点的关系,注意每一次的初始化要为1,否则无法进行大小统计。再在反图上做一遍DFS即可。

#include<bits/stdc++.h> using namespace std; #define maxn 1200 int n,w; int ans=0,d=0; int q[maxn]={}; int v[maxn]={}; int V[maxn]={}; int In[maxn]={}; int cnt[maxn]={}; int CNT[maxn]={}; vector <int> a[maxn]; vector <int> A[maxn];  inline void read(int &readnum) { 	int s=0,w=1;char c=getchar(); 	while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();} 	while (c>='0' && c<='9') s=s*10+c-48,c=getchar(); 	readnum=s*w;return; }  inline int Topsort(void) { 	int sum=0; 	int h=1,t=0; 	for (int i=1;i<=n;++i)  	    if (!In[i]) q[++t]=i; 	for (;h<=t;++h) 	{ 		int p=q[h];sum++; 		for (int i=0;i<a[p].size();++i) 		{ 			int np=a[p][i]; 			In[np]--; 			if (!In[np]) q[++t]=np;  		} 	} 	return sum; }  void dfs(int x) { 	int sum=0; 	cnt[x]=1,v[x]=1; 	for (int i=0;i<a[x].size();++i) 	{ 		int np=a[x][i]; 		if (v[np]) continue; 		dfs(np); 		sum+=cnt[np]; 	} 	cnt[x]+=sum; }  void DFS(int X) { 	int SUM=0; 	CNT[X]=1,V[X]=1; 	for (int I=0;I<A[X].size();++I) 	{ 		int NP=A[X][I]; 		if (V[NP]) continue; 		DFS(NP); 		SUM+=CNT[NP]; 	} 	CNT[X]+=SUM; }  int main() { 	freopen("coin.in","r",stdin); 	freopen("coin.out","w",stdout); 	read(n),read(w); 	for (int i=0;i<maxn-10;++i) a[i].clear(); 	for (int i=0;i<maxn-10;++i) a[i].clear(); 	for (int i=1;i<=w;++i) 	{ 		int x,y; 		read(x),read(y); 		In[y]++; 		a[x].push_back(y); 		A[y].push_back(x); 	} 	int stp=Topsort(); 	if (stp<n) 	{ 		cout<<"IMPOSSIBLE\n"; 		return 0; 	} 	for (int i=1;i<=n;++i) 	{ 		memset(v,0,sizeof(v)); 		memset(V,0,sizeof(V)); 		dfs(i),DFS(i); 		int tmp=cnt[i]+CNT[i]; 		if (tmp>ans) ans=tmp,d=i; 	} 	cout<<d<<endl; 	fclose(stdin); 	fclose(stdin); 	return 0; } 

超级牛游戏

题目描述
现在有N(1 <= N <= 2000)头奶牛在玩 超级牛 游戏。每头奶牛有一个唯一的ID,ID范围是 1 … 2 ^ 30-1。
超级牛比赛是淘汰赛 - 每场比赛后,输者退赛,赢者继续留在比赛,直到只剩一队游戏结束。 输赢是FJ自己决定的,或者说结果可以任意决定!
比赛的积分规则十分奇葩:积分=第一队的ID XOR 第二队的ID。 比如,12队和20队打比赛,积分是24,因为01100 XOR 10100 = 11000。
FJ认为,分越高越刺激。所以他想让总积分最高。请帮助FJ设计比赛。
输入格式
第一行包含一个整数N
以下N行包含N个队伍的ID。
输出格式
一行,一个整数,表示答案。
样例数据
input

4
3
6
9
10
output
37

做法:亮亮连边,组成一个完全图,跑一遍最大生成树(或者发把左右边转成负数跑一遍最小生成树)即可。

#include<bits/stdc++.h> using namespace std; #define LL long long  struct edge { 	int x,y,v; }e[5000000]={}; LL sum=0; int tot=0,n; int fa[2200]={}; int Id[2200]={};   inline void read(int &readnum) { 	int s=0,w=1;char c=getchar(); 	while (c<'0' || c>'9') {if (c=='-') w=-1;c=getchar();} 	while (c>='0' && c<='9') s=s*10+c-48,c=getchar(); 	readnum=s*w; }  inline int get(int x) { 	if (fa[x]==x) return x; 	return fa[x]=get(fa[x]); }  inline bool cmp(edge a,edge b) { 	return a.v<b.v; }  int main() { 	freopen("superbull.in","r",stdin); 	freopen("superbull.out","w",stdout); 	read(n); 	for (int i=1;i<=n;++i) fa[i]=i; 	for (int i=1;i<=n;++i) read(Id[i]); 	for (int i=1;i<=n;++i)  	    for (int j=i+1;j<=n;++j) 	        e[++tot]=edge{i,j,-(Id[i]^Id[j])}, 	        e[++tot]=edge{j,i,-(Id[j]^Id[i])}; 	sort(e+1,e+tot+1,cmp);  	for (int i=1;i<=tot;++i) 	{ 		int fa1=get(e[i].x); 		int fa2=get(e[i].y); 		if (fa1==fa2) continue; 		fa[fa1]=fa2; 		sum+=(LL)e[i].v;  	} 	cout<<-sum<<endl; 	fclose(stdin); 	fclose(stdout); 	return 0; } 

damage

题目描述
农夫John的农场遭受了一场地震.有一些牛棚遭到了损坏,但幸运地,所有牛棚间的路经都还能使用.
FJ的农场有P(1 <= P <= 30,000)个牛棚,编号1…P. C(1 <= C <= 100,000)条双向路经联 接这些牛棚,编号为1…C. 路经i连接牛棚a_i和b_i (1 <= a_i<= P;1 <= b_i <= P).路经 可能连接a_i到它自己,两个牛棚之间可能有多条路经.农庄在编号为1的牛棚.
N (1 <= N <= P)头在不同牛棚的牛通过手机短信report_j(2 <= report_j <= P)告诉FJ它 们的牛棚(report_j)没有损坏,但是它们无法通过路径和没有损坏的牛棚回到到农场.
当FJ接到所有短信之后,找出最小的不可能回到农庄的牛棚数目.这个数目包括损坏的牛棚.
输入格式
第1行: 三个空格分开的数: P, C, 和 N
第2…C+1行: 每行两个空格分开的数: a_i 和 b_i
第C+2…C+N+1行: 每行一个数: report_j
输出格式
第1行: 一个数,最少不能回到农庄的牛的数目(包括损坏的牛棚).
样例数据
input

4 3 1
1 2
2 3
3 4
3
output
3

我们知道对于每一个损坏的点,跑不出去,周围的一个点一定是损坏的。
因此我们只需要把每一个损坏了的点的边上的v数组标记为1,再把自己标记为1,直接用bfs跑一遍遍历即可。

#include<bits/stdc++.h> using namespace std;  int p,c,n,ans=0; int d[100000]; int v[100000]={}; int q[200000]={}; vector<int> a[100000]={};  inline void read(int &readnum) { 	int s=0,w=1;char c=getchar(); 	while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();} 	while (c>='0' && c<='9') s=s*10+c-48,c=getchar(); 	readnum=s*w;return; }  int main() { 	freopen("damage..in","r",stdin); 	freopen("damage..out","w",stdout); 	read(p),read(c),read(n); 	for (int i=1;i<=c;++i) 	{ 		int x,y; 		read(x),read(y); 		a[x].push_back(y); 		a[y].push_back(x); 	} 	for (int i=1;i<=n;++i) 	{ 		int k;read(k); 		for (int i=0;i<a[k].size();++i) v[a[k][i]]=1; 	} 	int h,t;t=h=1; 	memset(d,100,sizeof(d)); 	d[1]=0,v[1]=1,q[1]=1; 	while (h<=t) 	{ 		int p=q[h++]; 		for (int i=0;i<a[p].size();++i) 		{ 			int np=a[p][i]; 			if (v[np]) continue; 			d[np]=d[p]+1,v[np]=1,q[++t]=np; 		} 	} 	for (int i=1;i<=p;++i)   	    if (d[i]>1e6) ans++; 	cout<<ans<<endl;  	return 0; } 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!