Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 904 Accepted Submission(s): 338
题意:每条边过且只过一次,问至少要画几笔才能全部边都经过。。孤立的点忽视。
分析:首先,你用笔来画的话,只可能有2种,一:每回路,a——>b 二:形成回路,a——>...——>a
对于图中的每一块,度数数为奇数的点必须是由第一种画出来的,所以奇数/2就是画的笔数
由两种结合而成的图,也只是奇数/2
特别的,如果图只有第二种的话,即该块中不存在奇数点,则只要画一笔
对于整副图(每一块块组合而成),等于 :第一块奇数点/2+第二块奇数点/2+.......,最后得,图的总奇数点/2
接着还要计算有多少块里不存在奇数点(不存在奇数点的那块中,一定没有第一种画法,只需要画一笔),累加起来就得到答案了。。
(如果是个欧拉回路一笔就可以完成,如果是个其它连通集,要根据这个集合的奇度数而定,笔划数=奇度数/2,用并查集来判断有多少个连通集,然后用vector来存这些连通集,通过判断度数是奇偶性来确定是否为欧拉回路;总之笔划数 = 奇度数/2 + 欧拉回路数;)
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int N=100010; int n,m; int father[N],deg[N],odd[N],vis[N]; vector<int> vt; void makeSet(){ for(int i=1;i<=n;i++) father[i]=i; } int findSet(int x){ if(x!=father[x]){ father[x]=findSet(father[x]); } return father[x]; } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ vt.clear(); memset(vis,0,sizeof(vis)); memset(deg,0,sizeof(deg)); memset(odd,0,sizeof(odd)); makeSet(); int x,y; while(m--){ scanf("%d%d",&x,&y); deg[x]++; deg[y]++; int fx=findSet(x); int fy=findSet(y); if(fx!=fy) father[fx]=fy; } for(int i=1;i<=n;i++){ int fi=findSet(i); if(!vis[fi]){ vt.push_back(fi); //vt保存着集合,集合就是图 vis[fi]=1; } if(deg[i]%2==1) odd[fi]++; //保存这个集合的奇数度数的个数 } int ans=0; for(int i=0;i<(int)vt.size();i++){ int tmp=vt[i]; if(deg[tmp]==0) //孤立点 continue; if(odd[tmp]==0) //该集合是欧拉回路,有一条路 ans++; else ans+=odd[tmp]/2; } printf("%d\n",ans); } return 0; }