图论方面的题目,感觉都是比较基础的。除了08年的欧拉回路,考的最多的就是最小生成树的Prim或者Kruscal算法。大家在平时的训中
要注重《数据结构》中关于图论所涉及的基本算法,编熟了这些基本算法,感觉应付复试应该不成问题。
一,2005年 畅通工程(http://acm.hdu.edu.cn/showproblem.php?pid=1232)
其中要求的其实就是最小连通图的边数,一般就是最小生成树的边的个数(即:N个节点的最小生成树的边数为N-1)。根据给出的边情况,可能等价于先求出连通分量的个数M,所需的M-1条边最小生成树。
技巧:在同一个连通分量(集合)的节点对应的数组的值相同。合并集合,等于把他们对应的数组值设为相同即可。
#include<stdio.h> int x[1000]={0}; int n; void InitX() // initiate x array { for(int i=1;i<1000;i++) x[i] = i; } void ChangeX(int e,int r) { for(int i=1;i<=n;i++) { if(x[i] == e) x[i]=r; } } int main() { int m = 0; //the number of edge int xi; //the number of set's kind int a,b; while(scanf("%d",&n)&&n!=0) { InitX(); xi = n; scanf(" %d",&m); while(m--) { scanf("%d%d",&a,&b); if(x[a] != x[b]) //if a and b are not in the same set { ChangeX(x[b],x[a]); xi--; } } printf("%d/n",xi-1); } return 0; }
二,2006年 还是畅通工程(http://acm.hdu.edu.cn/showproblem.php?pid=1233)
利用的是Kruscal算法,可以说是比较典型的求最小生成树的题目。使用了直接插入排序算法进行排序,技巧和上一个算法的相似,也是数组标记。
#include<stdio.h> #include<string.h> struct edge { int n1; int n2; int lenth; }; edge edges[4950]; int x[100]; int xi; int m; int xed; static bool flg = true; void InsertX(int n1,int n2,int lenth,int e) //direct insert sort { if(flg) { flg=false; edges[1].n1=n1; edges[1].n2=n2; edges[1].lenth=lenth; return; } for(int j=e;j>=0;j--) { if(edges[j].lenth<=lenth)break; } for(int k=e;k>j;k--) { edges[k+1]=edges[k]; } edges[j+1].n1=n1; edges[j+1].n2=n2; edges[j+1].lenth=lenth; } void ChangeX(int e,int r) { for(int i=1;i<=xi;i++) { if(x[i] == e) x[i]=r; } } int main() { int n1,n2,lenth; int minSumlenth; while(scanf("%d",&xi)&&xi!=0) { for(int w=1;w<100;w++) x[w]=w; memset(edges,0,4950); m = xi*(xi-1)/2; flg = true; int i = 1; while(i<=m) { scanf("%d%d%d",&n1,&n2,&lenth); InsertX(n1,n2,lenth,i-1); i++; } x[1]=1; //add the first node to has_set xed=1; minSumlenth =0; for(int j=1;j<=m;j++) //notice { if(x[edges[j].n1]!=x[edges[j].n2]) { minSumlenth+=edges[j].lenth; ChangeX(x[edges[j].n2],x[edges[j].n1]); xed++; } else continue; } printf("%d/n",minSumlenth); } return 0; }
我一开始利用prime算法编写了一个,但放上去报错。至今没有找到错在哪里,下面贴上代码,希望有人能帮我看看。在此谢过了...
#include<stdio.h> #include<string.h> struct edge { int lenth; int visit; }; edge map[105][105]; int x[105],y[105]; int xi; int m; int xed; int main() { int n1,n2,lenth; int minSumlenth; while(scanf("%d",&xi)&&xi!=0) { for(int j=0;j<105;j++) //initiate map memset(map[j],0,105); memset(x,0,105); m = xi*(xi-1)/2; int i = 1; while(i<=m) { scanf("%d%d%d",&n1,&n2,&lenth); map[n1][n2].lenth = map[n2][n1].lenth =lenth; map[n1][n2].visit = map[n2][n1].visit =0; i++; } x[1]=1; //add the first node to has_set y[1]=1; xed=1; minSumlenth =0; while(xed != xi) { int minx,miny,min; bool flg = true; for(int k=1;k<=xed;k++) { for(int t=y[k]+1;t<=xi;t++) { if(map[t][y[k]].visit==0&&x[t]==0) { if(flg) { flg=false;miny=y[k];minx=t;min = map[t][y[k]].lenth; } if(map[t][y[k]].lenth<min) { miny=y[k];minx=t;min = map[t][y[k]].lenth; } } } } minSumlenth+=map[minx][miny].lenth; map[minx][miny].visit = 1; y[++xed]=minx; x[minx]=1; } printf("%d/n",minSumlenth); } return 0; }
三,2007年 畅通工程(http://acm.hdu.edu.cn/showproblem.php?pid=1863)
还是一样的,没有被AC,但机子上运行是正确的。天哪~ 怎么办哟!老是这个问题...
#include<stdio.h> #include<string.h> struct edge { int n1; int n2; int lenth; }; edge edges[4950]; int x[100]; int xi; int m; int xed; static bool flg = true; void InsertX(int n1,int n2,int lenth,int e) //direct insert sort { if(flg) { flg=false; edges[1].n1=n1; edges[1].n2=n2; edges[1].lenth=lenth; return; } for(int j=e;j>=0;j--) { if(edges[j].lenth<=lenth)break; } for(int k=e;k>j;k--) { edges[k+1]=edges[k]; } edges[j+1].n1=n1; edges[j+1].n2=n2; edges[j+1].lenth=lenth; } void ChangeX(int e,int r) { for(int i=1;i<=xi;i++) { if(x[i] == e) x[i]=r; } } bool OK() { for(int i=2;i<=xi;i++) { if(x[i-1]!=x[i])return false; } return true; } int main() { int n1,n2,lenth; int minSumlenth; while(scanf("%d%d",&m,&xi)&&m!=0) { for(int w=1;w<=xi;w++) x[w]=w; memset(edges,0,4950); flg = true; int i = 1; while(i<=m) { scanf("%d%d%d",&n1,&n2,&lenth); InsertX(n1,n2,lenth,i-1); i++; } x[1]=1; //add the first node to has_set xed=1; minSumlenth =0; for(int j=1;j<=m;j++) { if(x[edges[j].n1]!=x[edges[j].n2]) { minSumlenth+=edges[j].lenth; ChangeX(x[edges[j].n2],x[edges[j].n1]); xed++; } else continue; } if(!OK()) printf("?/n"); else printf("%d/n",minSumlenth); } return 0; }
四,2008年 欧拉回路(http://acm.hdu.edu.cn/showproblem.php?pid=1878)
此题是一道简单的图论题。判断一个图是否存在欧拉回路的充要条件是:1)每个点的度数为偶数,2)图是连通
定理的证明参照:http://zh.wikipedia.org/wiki/%E4%B8%80%E7%AC%94%E7%94%BB%E9%97%AE%E9%A2%98
粗略看了下给的答案解析,对于这道题的答案比较复杂。我的答案(数组的方法)虽然简单,但好像只能适用于无向图。以后做题的时候还得好好注意下!
#include<stdio.h> #include<string.h> int x[1002]; int y[1002]; int N,M; void ChangeSet(int e,int r) { for(int i=1;i<=N;i++) if(x[i]==e) x[i]=r; } void InitX() { memset(y,0,1002); for(int i=1;i<=102;i++) x[i]=i; } bool OK() { if(y[1]%2 != 0)return false; for(int i=2;i<=N;i++) { if(x[i-1]!=x[i]||y[i]%2!=0) { return false; } } return true; } int main() { int n1,n2; while(scanf("%d",&N)&&N!=0) { InitX(); scanf("%d",&M); while(M--) { scanf("%d%d",&n1,&n2); if(x[n1]!=x[n2]) { ChangeSet(x[n2],x[n1]); } y[n1]++; y[n2]++; } if(OK()) printf("1/n"); else printf("0/n"); } return 0; }
五,2008年 继续畅通工程(http://acm.hdu.edu.cn/showproblem.php?pid=1879)
做这道题经历了三个阶段,总结了三条经验:
1)逻辑思维构想阶段:刚开始想得过于复杂,越想觉得这道题越难。后来再仔细看了看题目,换了个角度,其实可以直接令已建的路成本为0,加以考虑。
2)节省步骤,跳跃式节省运行时间:另它为0,再插入数组中。再加入处理,还不如直接将它视为连通就可以了。
3)利用STL,节省运行时间:前段时间对STL并不是非常重视,直到今天遇到这个问题才发现,它的排序效率和操作步骤是多么的节省时间。比我的直接插入排序要快的多,看来要好好学习一下了!(拼命捡呀...)
以下是被AC的代码。
#include<stdio.h> #include<string.h> struct edge { int n1; int n2; int lenth; }; edge edges[4950]; int x[100]; int xi; int m; int xed; static bool flg = true; void InsertX(int n1,int n2,int lenth,int e) //direct insert sort { if(flg) { flg=false; edges[1].n1=n1; edges[1].n2=n2; edges[1].lenth=lenth; return; } for(int j=e;j>=0;j--) { if(edges[j].lenth<=lenth)break; } for(int k=e;k>j;k--) { edges[k+1]=edges[k]; } edges[j+1].n1=n1; edges[j+1].n2=n2; edges[j+1].lenth=lenth; } void ChangeX(int e,int r) { for(int i=1;i<=xi;i++) { if(x[i] == e) x[i]=r; } } bool OK() { for(int i=2;i<=xi;i++) { if(x[i-1]!=x[i])return false; } return true; } int main() { int n1,n2,lenth; int minSumlenth; while(scanf("%d%d",&m,&xi)&&m!=0) { for(int w=1;w<=xi;w++) x[w]=w; memset(edges,0,4950); flg = true; int i = 1; while(i<=m) { scanf("%d%d%d",&n1,&n2,&lenth); InsertX(n1,n2,lenth,i-1); i++; } x[1]=1; //add the first node to has_set xed=1; minSumlenth =0; for(int j=1;j<=m;j++) { if(x[edges[j].n1]!=x[edges[j].n2]) { minSumlenth+=edges[j].lenth; ChangeX(x[edges[j].n2],x[edges[j].n1]); xed++; } else continue; } if(!OK()) printf("?/n"); else printf("%d/n",minSumlenth); } return 0; }