图论专题整理[主攻洛谷

  • 洛谷例题【p2661 信息传递

  • 大佬思路:并查集

  • 附上题解【来自洛谷用户 【bie淖_kkk&anyway】

并查集求最小环

把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。

图论求最小环,我在里面看到了并查集。

假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。

这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。

如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。

代码如下

// luogu-judger-enable-o2
#include

using namespace std;
int n,ti;
const int maxn=200000+5;
int fa[maxn],dis[maxn],ans;
int find(int x)
{
    if(fa[x]!=x) 
    {
        int f=fa[x];
        fa[x]=find(fa[x]);
        dis[x]+=dis[f];
    }
    return fa[x];
}
void check(int x,int y)
{
    int a=find(x),b=find(y);
    if(a!=b) {
    fa[a]=b;
    dis[x]=dis[y]+1;}
    else ans=min(ans,dis[x]+dis[y]+1);
    return;
}
int main()
{
     std::ios::sync_with_stdio(false);
     cin>>n;
     for(int i=1;i<=n;i++) 
     {
     	fa[i]=i;
     }
     ans=maxn;
      for(int i=1;i<=n;i++) 
     {
       cin>>ti;
       check(i,ti);
     }
     cout<

例题2【P1330 封锁阳光大学

  • 染色+dfs

良心地附上题目

题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

阳光大学的校园是一张由N个点构成的无向图,N个点之间由M条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在与这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

输入输出格式 输入格式: 第一行:两个整数N,M

接下来M行:每行两个整数A,B,表示点A到点B之间有道路相连。

输出格式: 仅一行:如果河蟹无法封锁所有道路,则输出“Impossible”,否则输出一个整数,表示最少需要多少只河蟹。

输入输出样例 输入样例#1: 3 3 1 2 1 3 2 3 输出样例#1: Impossible 输入样例#2: 3 2 1 2 2
3 输出样例#2: 1 说明 【数据规模】

1<=N<=10000,1<=M<=100000,任意两点之间最多有一条道路。

前置知识点
前向星存边

  1. 结构
    这里用两个东西:

1 结构体数组edge存边,edge[i]表示第i条边,

2 head[i]存以i为起点的第一条边(在edge中的下标)

struct EDGE{ int next; //下一条边的存储下标(默认0) int to; //这条边的终点
int w; //权值 }; EDGE edge[500010];

2.增边 若以点i为起点的边新增了一条,在edge中的下标为j.

那么edge[j].next=head[i];然后head[i]=j.

即每次新加的边作为第一条边,最后倒序遍历

void Add(int u, int v, int w) { //起点u, 终点v, 权值w //cnt为边的计数,从1开始计
edge[++cnt].next = head[u]; edge[cnt].w = w; edge[cnt].to = v;
head[u] = cnt; //第一条边为当前边 }

  1. 遍历 遍历以st为起点的边

for(int i=head[st]; i!=0; i=edge[i].next) i开始为第一条边,每次指向下一条(以0为结束标志)
(若下标从0开始,next应初始化-1)

作者:_Gion
来源:CSDN
原文:https://blog.csdn.net/Binary_Heap/article/details/78209086

**题解的解释 **

原题解:洛谷置顶的那条 来自【KesdiaelKen
解释来自【InnovatorNZ

原置顶的那个题解已经说的很详尽了。这里我想对那个题解中部分代码进行一下说明: Q1: 为什么dfs结尾处没有回溯操作? (used[u]=false) A1: 这道题不用回溯。只有在那些需要求出所有可能路径的题目中才需要回溯操作。为什么呢?其实回溯操作类似于一个“遗忘操作”;使得在寻找第二条路径时可以找到这个相同的结点。而这道题并不需要求出所以可能路径。不明白的同学可以拿起纸和笔模拟一下:从根结点开始,依次往下,颜色分别为黑、白、黑……直到最后一个结点。可见这个过程只是把图中所有的结点都走一遍,dfs return的时候也是如此,只是为了把所有的结点都走一遍,并不是要求出所有可能路径。因此并不需要回溯。 Q2: 如果不需要求出所有路径,那怎么保证答案一定是最小的那个呢? A2: 其实如果拿起纸和笔模拟一下你会发现其实这个过程求出的答案是唯一的,也一定是最小的。不过注意一点,有一行很重要:ans+=min(sum[white],sum[black]). 这句话说明了应该加的是黑和白中更小答案的那个。至于为什么很好理解:你应该在染黑色的结点们和染白色的结点们中更小的放河蟹,因为两种方法都满足题目要求。 Q3: 为什么main中还需要再把所有结点都dfs一遍? A3: 因为图不一定是全联通的。至于会不会超时/重复计算,你不需要担心。此前说明过了,不需要回溯操作;因此main中再dfs时会检查该结点是否被访问过了,如果访问过了就直接跳过,即if(used[i]) continue;.

附AC代码

#include

using namespace std;
const int maxn=200000+5;
struct Edge{
	int w,next;
}ed[maxn];
int head[20005];
int cnt=0;
bool vis[20005];
int col[20005];
int sum[2];
void add(int a,int b)
{
	
	cnt++;
	ed[cnt].w=a;
	ed[cnt].next=head[b];
	head[b]=cnt;
	return ;
}
int n,m,ans;
int a,b;
bool color(int edge,int colour)
{
   if(vis[edge])
   {
   	if(col[edge]==colour)  return true;
   	else return false;
	   }
	   vis[edge]=true;
	   sum[col[edge]=colour]++;
	   bool flag=true;
	   for(int i=head[edge];i&&flag;i=ed[i].next)
	   {
	   	flag=flag&&color(ed[i].w,1-colour);
	   }
	   return flag;
}



int main()
{
	std::ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		sum[0]=sum[1]=0;
		if(!color(i,0)) 
		{
			cout<<"Impossible";
			return 0;
		}
		ans+=min(sum[0],sum[1]);
	}
	cout<
  1. P1341 无序字母对【待解决(据说是欧拉回路模板
    但是还是GG了
    附20分代码QAQ

    #include

    using namespace std;
    const int maxn=10000+10;
    int  f[maxn][maxn];
    char de[maxn];
    int n,cnt,sum,h;
    char ans[maxn];
    void find(int i)
    {
    	for(int j=1;j<=150;j++)
    	{
    		if(f[i][j])
    		f[i][j]--;
    		f[j][i]--;
    		find(j);
    	}
    	ans[++cnt]=i;
    	return;
    }
    int main() 
    {
    	std::ios::sync_with_stdio(false);
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		string s;
    		cin>>s;
    		f[s[0]][s[1]]++;
    	    f[s[1]][s[0]]++;
    	    de[s[0]]++;
    	    de[s[1]]++;
    	}
    	for(int i=1;i<=150;i++)
    	if(de[i]%2==1){
    		sum++;
    	if(!h) h=i;}
    	if(!h) 
    	for(int i=0;i<150;i++)
    	if(de[i])  {
    	h=i;
    	break;}
    	if(sum&&sum!=2){
    	 cout<<"No Solution";
    	 return 0;}
    	 find(h);
    	if(cnt=1;i--)
    	 cout<

八个点MLE为哪般 (现在怀疑是变量类型定义错了

附上重构后AC代码

#include

using namespace std;
int G[257][257],de[257],n;
char zi[257],ans[257*257];
void dfs(int i)
{
	for(int j=0;j<257;j++)
	{
		if(G[i][j])
		{
			G[i][j]=G[j][i]=0;
			dfs(j);
		}
	}
	ans[n--]=i;
}
int main()
{
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=0;i>zi;
	G[zi[0]][zi[1]]=G[zi[1]][zi[0]]=1;
	de[zi[0]]++;
	de[zi[1]]++;}
	char a=0,cnt=0;
	for(int i=0;i<257;i++)
	
	{
		if(de[i]%2&1)
		{
			cnt++;
			if(!a) a=i;
		}
	}
	if(!a) for(int i=0;i<257;i++)
	if(de[i])
	{
		a=i;
		break;
	}
	if(cnt&&cnt!=2)
	{
		cout<<"No Solution";
		return 0;
	}
	dfs(a);
	puts(ans);
	return 0;
	
}
  • P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm

  • 单链/环+蜜汁信息传递
    -附上AC代码

#include


using namespace std;
const int maxn=100000+5;
int n,to[maxn],dis[maxn],ans[maxn];
bool vis[maxn];
void dfs(int now,int dep)
{
	vis[now]=true;
	dis[now]=dep;
	if(ans[to[now]]) ans[now]=ans[to[now]]+1;
	else if(dis[to[now]]) 
	{
		ans[now]=dis[now]-dis[to[now]]+1;
		int nex=to[now];
		while(nex!=now)
		{
			ans[nex]=ans[now];
			nex=to[nex];
		}
	}
	else {
		dfs(to[now],dep+1);
		if(!ans[now]) ans[now]=ans[to[now]]+1;
	}
}
int main(){
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>to[i];
	for(int i=1;i<=n;i++)
	{
		if(!vis[i]) dfs(i,0);
	}
	for(int i=1;i<=n;i++)
	cout<

**

最短路问题

**

  • P3371 【模板】单源最短路径(弱化版)
  • 真 板子
  • 附AC代码
#include 

using namespace std;
int n,m,s;
bool vis[20000];
long long dis[20000],nin;
struct Edge{
	int z;
	int w;
	int next;
}edge[1000000];
int head[20000];
int cnt;
inline void add(int x,int y,int z)
{
	 edge[++cnt].z=y;
	 edge[cnt].w=z;
	 edge[cnt].next=head[x];
	 head[x]=cnt;
}
int main()
{
	std::ios::sync_with_stdio(false);
	cin>>n>>m>>s;
	for(int i=1;i<=n;i++) dis[i]=2147483647;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	int cur=s;
	dis[s]=0;
	while(!vis[cur])
	{
		vis[cur]=true;
		for(int i=head[cur];i!=0;i=edge[i].next)
		{
			if(!vis[edge[i].z]&&dis[edge[i].z]>dis[cur]+edge[i].w)
			dis[edge[i].z]=dis[cur]+edge[i].w;
		}
	
	nin=2147483647;
	for(int i=1;i<=n;i++)
	{
		if(!vis[i]&&nin>dis[i])
		{
			nin=dis[i];
			cur=i;
		}
	}
  

  }

    for(int i=1;i<=n;i++)
    cout<

改了改代码就A了热浪

权值是1的最短路水题(误

  1. P1346 电车

题目描述
在一个神奇的小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能)。在每个路口,都有一个开关决定着出去的轨道,每个开关都有一个默认的状态,每辆电车行驶到路口之后,只能从开关所指向的轨道出去,如果电车司机想走另一个轨道,他就必须下车切换开关的状态。

为了行驶向目标地点,电车司机不得不经常下车来切换开关,于是,他们想请你写一个程序,计算一辆从路口A到路口B最少需要下车切换几次开关。

输入输出格式 输入格式: 第一行有3个整数2<=N<=100,1<=A,B<=N,分别表示路口的数量,和电车的起点,终点。

接下来有N行,每行的开头有一个数字Ki(0<=Ki<=N-1),表示这个路口与Ki条轨道相连,接下来有Ki个数字表示每条轨道所通向的路口,开关默认指向第一个数字表示的轨道。

输出格式: 输出文件只有一个数字,表示从A到B所需的最少的切换开关次数,若无法从A前往B,输出-1。

  • 思路是把默认开关默认轨道的权值设为0,其他为1,然后就可以愉快地水题了。
    附AC代码
#include
#include
using namespace std;
const int maxn=100+5,INF=1e+8;
int k[maxn][maxn];
int n,a,b,ki,z;
int main()
{
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
	k[i][j]=INF;
	k[i][i]=0;} 
    for(int i=1;i<=n;i++){
    cin>>ki;
    for(int j=1;j<=ki;j++){
    cin>>z;
    if(j==1)k[i][z]=0;
    else k[i][z]=1;
    }
    }
    for(int h=1;h<=n;h++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    k[i][j]=min(k[i][j],k[i][h]+k[h][j]);
    if(k[a][b]==INF) cout<<"-1";
    else cout<

类题【P1135 奇怪的电梯

  • 改了改代码就A了
  • 总体思路差不太多,主要是加两个特判,保证电梯可以到达即可。
#include
#include
using namespace std;
const int maxn=200+5,INF=1e+8;
int k[maxn][maxn];
int n,a,b,ki,z;
int main()
{
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
	k[i][j]=INF;
	k[i][i]=0;} 
    for(int i=1;i<=n;i++)
   {
	cin>>z;
    if(i+z<=n) k[i][z+i]=1;
    if(i-z>=1) k[i][i-z]=1;
    
    }
    
    for(int h=1;h<=n;h++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    k[i][j]=min(k[i][j],k[i][h]+k[h][j]);
    if(k[a][b]==INF) cout<<"-1";
    else cout<

丧心病狂的测试题

  • 【T3:P4306 [JSOI2010]连通数

度量一个有向图恋情情况的一个指标是连通,指途中可达点对的个数。

下图的连通数是14
图论专题整理[主攻洛谷_第1张图片

现在要你求出连通数

输入输出格式 输入格式: 输入数据第一行是图顶点的数量,一个正整数N。
接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

输出格式: 输出一行一个整数,表示该图的连通数。

输入输出样例 输入样例#1: 3 010 001 100 输出样例#1: 9 说明 对于100%的数据,N不超过2000。

  • 前置知识
  • 【bitset优化floyd求传递闭包】[真的好使!!!
  • bitset操作

b.any()

b中是否存在置为1的二进制位?

b.none()

b中不存在置为1的二进制位吗?

b.count()

b中置为1的二进制位的个数

b.size()

b中二进制位的个数

b[pos]

访问b中在pos处的二进制位

b.test(pos)

b中在pos处的二进制位是否为1?

b.set()

把b中所有二进制位都置为1

b.set(pos)

把b中在pos处的二进制位置为1

b.reset()

把b中所有二进制位都置为0

b.reset(pos)

把b中在pos处的二进制位置为0

b.flip()

把b中所有二进制位逐位取反

b.flip(pos)

把b中在pos处的二进制位取反

b.to_ulong()

用b中同样的二进制位返回一个unsigned long值

os << b

把b中的位集输出到os流

然后就A了我人生中第一道紫题
附AC代码

#include 

using namespace std;
const int maxn=2000+5;
char s[maxn];
bitsetf[maxn];
int n; 
long long ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		for(int j=1;j<=n;j++)
		if(s[j-1]=='1'||j==i) f[i][j]=1;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	if(f[j].test(i)) f[j]|=f[i];
	for(int i=1;i<=n;i++) ans+=f[i].count();
	cout<

你可能感兴趣的:(图论专题整理[主攻洛谷)