从搜索到图论

图,就是点用线连起来,相当于二叉树的一种进化,二叉树的父子关系非常明显,而图论就有不怎么明显(乱伦)

对于图的一些题目,大多数都是用搜索(如果不懂搜索的童鞋,来看看吧-->点击打开链接)来解决

其大致方法可分为两类:


一、邻接矩阵

将每一个点是否可以到另一个点,用bool变量来存储,比如:a[ i ][ j ] = 1,表示从 i 到 j 可以走,在进行搜索

邻接矩阵有一个缺点,当在边比较稀疏的时候,邻接矩阵的搜索量会变得很大,所以,在更多时候,我们会运用第二种方法


二、邻接表

将一个点可以到达的另一个点的另一个点的坐标存储一下,如:a[ i ] [ ++a[ 0 ] ] = j 表示 i 可以到 j 

这种方法在边比较稀疏时,就不会再盲目的搜索不可以走到的点了


其中关于图论的经典例题,必须得介绍两位数学家:


欧拉

大家肯定听说过七桥问题吧,都知道这个假设是不可能的,其中欧拉找到了这个问题的解决方案

首先,每个点如果可以通向奇数个点,则称这个点为奇点(任何一个无向图都有偶数个奇点),如果一个图没有奇点,则从任何一个点开始,都可以一笔画

如果有两个奇点,则从其中任意一个奇点出发,可以一笔画,但如果有4个及以上的奇点,则无法一笔画


这就是欧拉回路


一笔画问题(euler-circuit.cpp)


题目描述
对给定的一个无向图,判断能否一笔画出。若能,输出一笔画的先后顺序,否则输出“No Solution!”

所谓一笔画出,即每条边仅走一次,每个顶点可以多次经过。

输出字典序最小的一笔画顺序。

输入
第1行:1个整数n,表示图的顶点数(n<=100)

接下来n行,每行n个数,表示图的邻接矩阵

输出
第1行:一笔画的先后顺序,每个顶点之间用一个空格分开

样例输入
样例一
3
0 1 1 
1 0 1 
1 1 0 
样例二:
7
0 1 0 1 1 0 1 
1 0 1 0 0 0 0 
0 1 0 1 0 0 0 
1 0 1 0 0 0 0 
1 0 0 0 0 1 0 
0 0 0 0 1 0 1 
1 0 0 0 0 1 0 
样例输出
样例一:
1 2 3 1
样例二:
1 2 3 4 1 5 6 7 1

这道题可以将边用数组存储,用深搜来实现,如果害怕超时的话,可以设一个bool变量,当输出了第一个方案后,则改变bool变量,不再继续搜索,直到return

代码如下:

<span style="font-size:14px;background-color: rgb(255, 255, 153);">#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,ways[101],all,f[101],g;
bool road[101][101],p;
void work(int t)
{
	int i;
	if(!all)
	{
		if(!p)
			for(i=1;i<=g;i++)
				printf("%d ",f[i]);
		p=1;
		return;
	}
	for(i=1;i<=n;i++)
		if(road[t][i])
		{
			road[t][i]=0;
			road[i][t]=0;
			all--;
			f[++g]=t;
			work(i);
			f[g--]=t;
			all++;
			road[t][i]=1;
			road[i][t]=1;
		}
}
int main()
{
	int i,j,k;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		{
			scanf("%d",&k);
			if(k)
			{
				road[i][j]=1;
				ways[i]++;
				if(i<j)
					all++;
			}
		}
	k=0;
	for(i=1;i<=n;i++)
	{
		if(ways[i]%2)
			k++;
	}
	if(k>2||k==1)
	{
		printf("No Solution!");
		return 0;
	}
	if(!k)
	{
		work(1);
		printf("1");
	}
	else
	{
		for(i=1;i<=n;i++)
			if(ways[i]%2)
			{
				work(i);
				for(j=i+1;j<=n;j++)
					if(ways[j]%2)
					{
						printf("%d",j);
						break;
					}
				break;
			}
	}
}</span>

输出什么的一看就懂啦~\( ≧ ▽ ≦ )/~

哈密顿(又称哈密尔顿)
他的回路其实与欧拉差不多,只是他需要遍历的是点,而不是边,没有什么技术含量,所以就解释到这儿

哈密顿路问题(hamilton.cpp)


题目描述
邮递员在送信时,为了节省路途,自己规定:每次总是从n个村子中选择其中一个合适的村子出发,途经每个村子仅且经过一次,送完所有的信。 已知各个村子的道路连通情况。请你帮邮递员选择一条合适的路线。

输入
第1行:整数n(2<=n<=200):村子的个数。

接下来是一个n*n的0、1矩阵(每2个数之间有1个空格),表示n个村子的连同情况,如:a[i,j]=1 ,表示第i和第j个村子之间有路可走,如果a[i,j]=0,表示他们之间无路可走。

输出
第1行:1个整数表示可行的方案总数。

样例输入
7
0 1 0 1 1 0 0
1 0 1 0 1 0 0
0 1 0 0 0 0 1
1 0 0 0 0 0 0
1 1 0 0 0 1 0
0 0 0 0 1 0 1
0 0 1 0 0 1 0

样例输出
8

跟第一道题差不多,把点用数组存储,用深搜遍历,每次搜完,总数就加加,输出就行了

代码如下:

<span style="font-size:14px;background-color: rgb(255, 255, 153);">#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct ill{
	int num;
	int ways[101];
	bool v;
}point[101];
int n,all,k;
void work(int h)
{
	int i;
	if(!all)
	{
		k++;
		return;
	}
	if(!point[h].v)
	{
		point[h].v=1;
		all--;
		for(i=1;i<=point[h].num;i++)
			work(point[h].ways[i]);
		all++;
		point[h].v=0;
	}
}
int main()
{
	int i,j;
	scanf("%d",&n);
	all=n;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		{
			scanf("%d",&k);
			if(k)
			{
				point[i].ways[++point[i].num]=j;
			}
		}
	k=0;
	for(i=1;i<=n;i++)
		if(point[i].num%2)
			k++;
	if(!k)
	{
		work(1);
		printf("%d",k);
	}
	else
	{
		k=0;
		int 
		work(1);
		printf("%d",k);
	}
}</span>
没什么难度,所以,就这样啦O(∩_∩)O~ 

posted @ 2016-10-17 13:09  Izayoi_Doyo  阅读(122)  评论(0编辑  收藏  举报