图,就是点用线连起来,相当于二叉树的一种进化,二叉树的父子关系非常明显,而图论就有不怎么明显(乱伦)
对于图的一些题目,大多数都是用搜索(如果不懂搜索的童鞋,来看看吧-->点击打开链接)来解决
其大致方法可分为两类:
一、邻接矩阵
将每一个点是否可以到另一个点,用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
跟第一道题差不多,把点用数组存储,用深搜遍历,每次搜完,总数就加加,输出就行了
代码如下:
没什么难度,所以,就这样啦O(∩_∩)O~<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>