从搜索到图论
图,就是点用线连起来,相当于二叉树的一种进化,二叉树的父子关系非常明显,而图论就有不怎么明显(乱伦)
对于图的一些题目,大多数都是用搜索(如果不懂搜索的童鞋,来看看吧-->点击打开链接)来解决
其大致方法可分为两类:
一、邻接矩阵
将每一个点是否可以到另一个点,用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~