hdu3018 一笔画问题

题意:
      给你一幅画,这幅画由点和边构成,问你最少几笔能把这幅画画完。


思路:
     这个题目的结论比较巧妙,首先我们考虑下,如果给的图是欧拉图,或者是条欧拉回路,那么我们一笔就搞定了,那么我们可以把图的每一个部分<联通快>都检查一遍,如果是

连通块,那么就直接+1,如果不是欧拉回路<包括欧拉路>,那么我们就要统计这个连通块的度数为奇数的点的个数s, 答案+s/2,原因是我们可以用s/2笔把所有的奇数度都填充完,一次填充两个,当最后一笔的时候整个连通块已经是一个欧拉回路了,然后我们必要把最后一笔抬起来,直接一笔画就行了,还有就是提示下s永远都是偶数,因为每当添加一笔的时候有四种情况新添加的这条边的两个端点 分别产生两个度数为奇数的 + 2 分别消除两个度数为奇数的 -2 分别一个消除,一个增加 +1-1 所以买个连通块的奇数度点的个数很定是偶数个,总之最优的策略就是用最少的步数把每个一个连通块变成欧拉回路,然后一笔搞定,还有就是要明确,我们并不是在填边,而是说需要填边的地方就是我们台笔的地方,也就是要多花费一笔的地方。还有提示一点,可能出现独立的一个点,独立的点不用画,其他的具体细节看代码。


#include<stdio.h>
#include<string.h>

#define N_node 110000
#define N_edge 110000

typedef struct
{
   int to ,next;
}STAR;

STAR E[N_edge];
int list[N_node] ,tot;
int mer[N_node] ,du[N_node];

void add(int a ,int b)
{
   E[++tot].to = b;
   E[tot].next = list[a];
   list[a] = tot;
}

int finds(int x)
{
   return x == mer[x] ? x : mer[x] = finds(mer[x]);
}

int main ()
{
   int i ,j ,n ,m ,a ,b;
   while(~scanf("%d %d" ,&n ,&m))
   {
      for(i = 1 ;i <= n ;i ++) mer[i] = i;
      memset(du ,0 ,sizeof(du));
      for(i = 1 ;i <= m ;i ++)
      {
         scanf("%d %d" ,&a ,&b);
         mer[finds(a)] = finds(b);
         du[a] ++ ,du[b] ++;
      }
      memset(list ,0 ,sizeof(list)) ,tot = 1;
      for(i = 1 ;i <= n ;i ++)
      {
         int x = finds(i);
         if(x == i) continue;
         add(x ,i);
      }
      int Ans = 0;
      for(i = 1 ;i <= n ;i ++)
      {
         int tsum = 0;
         int x = finds(i);
         if(i != x || i == x && !list[x]) continue;
         tsum = du[i] % 2;
         for(int k = list[x] ;k ;k = E[k].next)
         tsum += du[E[k].to] % 2;
         (!tsum)? tsum ++ : tsum /= 2;
         Ans += tsum;
      }
      printf("%d
" ,Ans);
   }
   return 0;
}

原文地址:https://www.cnblogs.com/csnd/p/12062711.html