[置顶] hdu3018解题报告--也是白话几笔画学习总结
1.题意 2.分析难点(结合图形)
1.首先说说题意吧...题意很简单...但是一开始很菜的我就很迷惑..在想啊...题目怎么就会有没有连接边的点呢....因为你每次给出一条边..就把 a,b连接啦...所有...所有的城市最少都有一条边......(不知道有没有和我一样的人...)此处省略反省1W字........题目的城市是从1到N标记,比如有5个城市,两条街:1--2、3---4,那么 城市5就是不要考虑的.....
很明确...这就是求几笔画问题....很菜的我刚刚学习了欧拉回路....对于刚刚接触的我来说....不知道从哪里下手....于是...百度.....了解了一个联通图的:笔画次数=奇度顶点数/2;如果这个图没有奇度顶点,就可以一笔画.....
2.对于很菜的我来说难点就在于....如果N个城市,分成不同的“块”(就是不和别的连在一起、单独的一块城市),比如下图:
对于案例1分为了3个块,案例2分为了2个块,而且...对于案例1中的5号....我们要忽略,菜菜的我就会根据并査集判断一个图是否连通,然后对于连通的根据欧拉路的一些性质看看这个连通的图是不是欧拉路(之前发表过白话欧拉路径与欧拉回路)
,于是纠结了一下午...参考了一点点别人的代码....这里给出自己详细的理解、分析过程:
我们的目标是:(没蛀牙,小小玩笑)目标是求每个块中的 奇度点数/2,然后把所有块的这个奇度点数/2 加起来就是总共要几笔画;
1.我们有一个记录度的数组degree[MAX]..(不分入度和出度),案例输入之初赋值为0,案例每次输入a,b就degree[a]++,degree[b]++;那么我们可以知道,,从1到N,度为0的点,就是我们不要考虑的...那么
2.我们要考虑怎么分成不同的”块“,我们可以知道,对于每一个块...用并査集在输入的时候处理,那么,每一个块,就有一个祖先boss,我们就可以根据boss来分块,用一个flag[MAX],flag[i]表示以 i 为boss的这个块奇度点个数(因为我们要根据这个块来计算:笔画次数=奇度顶点数/2),因为我们在并査集操作过程中不确定会把谁作为祖先,那么数组大小开MAX,初始化为-1,然后我们从1到N每个点 i 找到祖先 x,如果flag[x]是-1,意思就是x第一次作为boss,并且在degree[i]不为0(为0就是要忽略的)的情况下吧flag[x]=0,然后我们在判断:如果degree[i]%2是不是0(奇度判断),决定flag[x]是不是要++一次(如果是奇度点,以x为boss的块中就多了一个奇度点);
3.最后再从1到N计算一次笔画就可以啦...
上马:
// 46MS 1400K #include<stdio.h> #include<string.h> #define MAX 100005 int N,M; int father[MAX]; int degree[MAX]; int flag[MAX]; int find(int a) { if(a!=father[a]) father[a]=find(father[a]); return father[a]; } void init() { int i; for(i=1;i<=N;i++) father[i]=i//,degree[i]=0,flag[i]=-1; memset(degree,0,sizeof(degree));memset(flag,-1,sizeof(flag));//事实证明memset比for从1到N初始快 int a,b; for(i=0;i<M;i++) { scanf("%d%d",&a,&b); degree[a]++;degree[b]++; father[find(a)]=find(b);//合并 } } void work() { int i; for(i=1;i<=N;i++) { int x=find(i); if(flag[x] == -1 && degree[i]!=0) flag[x]=0; if(degree[i]%2 != 0) flag[x]++; } int ans=0; for(i=1;i<=N;i++) if(flag[i]!=-1) { if(flag[i]==0) ans++;//|| flag[i]%2==0 else ans+=flag[i]/2; } printf("%d\n",ans); } int main() { while(~scanf("%d%d",&N,&M)) { init(); work(); } return 0; } /* 5 3 1 2 1 3 1 4 */
个人愚昧观点..欢迎指正,欢迎讨论...