201512-4 CSP 真题 送货
题目分析
学过离散数学应该对欧拉通路不陌生,这道题简单来说就是找一条欧娜通路出来,就是一笔画的问题,且要求从标号1开始,这种题只能用bfs。
dfs(深度优先搜索)
#include<iostream>
#include<algorithm>
using namespace std ;
const int N = 10010 , M = N * 2;
int h[N] , e[M], ne[M], idx ; // h是n个领接表的头,e存的每一个节点的值,ne存的是next指针
int ans = N ;
bool stu[N] ;
int n;
void add(int a , int b)
{
e[idx] = b ;
ne[idx] = h[a] ;
h[a] = idx++ ;
}
void dfs(int num)
{
stup[num] = true ;
for(int i = h[u] ; i != -1 ; i = ne[i])
{
int j = e[i] ; // 当前点
if (!stu[j])
{
dfs(i) ;
}
}
}
int main()
{
memset(h,-1,sizeof(h)) ;
// add操作
// dfs(1)
return 0 ;
}
上述代码是dfs的经典模板,可以去y总哪里学习一下,awing这个平台。
欧拉通路
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉通路。
如果一个回路是欧拉通路,则称为欧拉回路。
欧拉回路的判断:
无向图存在欧拉回路的充要条件:
一个无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数,且该图是连通图。
有向图存在欧拉回路的充要条件:
一个有向图存在欧拉回路,所有顶点的入度=出度且该图是连通图。
欧拉通路的判断(重点!!!!下面要用到)
无向图:
图连通;图中只有0个(即全是偶数节点)或有2个度为奇数的节点。
度为2的时候,这两个点分别为起点和终点
有向图:
图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1 或 所有节点入度=出度
此题,我们采取领接表存储,用stl中的vectoe Graph[],因为是无向图,注意加边的时候两边都要加上。
图的领接表存储
for (int i = 1 ; i <= m;++i){
int start , end ;
cin >> start >> end ;
Graph[start].push_back(end) ;
Graph[end].push_back(start) ;
}
判断有多少奇度点
将图存储好了之后,我们就要判断一个图有没有欧拉通路存在,注意,有欧拉回路一定有欧拉通路,所以当奇度点等于0和2的时候,我们认为它有欧拉通路,并且题目要求从1开始走,也就是从1开始遍历边,还要满足最小的点走,我们可以在遍历边的时候,将每个领接表中的点进行排序。例如Graph[1]中存的[4,1,5,6],排序完就是[1,4,5,6],那么从1开始遍历的时候,第一条边是1-4,如果将每个点都进行排序,就能保证我们每次走的边到的点都是最小的。判断是否为奇度点也很简单,用size对2模运算就行。
for (int i = 1 ; i <= n ; ++i){
sort(Graph[i].begin(),Graph[i].end()) ;
if (Graph[i].size() % 2 == 1){
k++;
if (k==1){
start = i ;
}
}
}
最终代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
// 这种类型的题多思考visited数组和ans类型放在递归的哪个位置
int n , m ;
vector<int> Graph[10001] ;
vector<int> ans ;
bool visited[10005][10005];
void dfs(int num){
int end ;
for (int i = 0 ; i < Graph[num].size() ; ++i){
end = Graph[num][i] ;
if (visited[num][end]==false){
visited[num][end]=visited[end][num] = true ;
dfs(end);
}
}
ans.push_back(num);
}
int main()
{
cin >> n >> m ;
for (int i = 1 ; i <= m;++i){
int start , end ;
cin >> start >> end ;
Graph[start].push_back(end) ;
Graph[end].push_back(start) ;
}
int k = 0 ;
int start = 1;
for (int i = 1 ; i <= n ; ++i){
sort(Graph[i].begin(),Graph[i].end()) ;
if (Graph[i].size() % 2 == 1){
k++;
if (k==1){
start = i ;
}
}
}
if (k==0||k==2){
dfs(start);
}
if (ans.size()==m+1){
// for (vector<int>::iterator it = ans.begin();it != ans.end() ; it++){
// cout << *it << " " ;
// }
for (vector<int>::iterator it = ans.end() - 1;it != ans.begin()-1 ; it--){
cout << *it << " " ;
}
}else{
cout << -1 ;
}
return 0 ;
}
总结
前几年的题目难度还比较简单,但是我还是没做出来,一开始的思想是用领结矩阵遍历点。。。。