HDU 3018 Ant Trip(一笔画问题)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3018

 

题意:每条边过且只过一次,问至少要画几笔才能全部边都经过。。孤立的点忽视。

 

分析:首先,你用笔来画的话,只可能有2种,一:每回路,a——>b  二:形成回路,a——>...——>a

 

对于图中的每一块,度数数为奇数的点必须是由第一种画出来的,所以奇数/2就是画的笔数

由两种结合而成的图,也只是奇数/2

特别的,如果图只有第二种的话,即该块中不存在奇数点,则只要画一笔

 

对于整副图(每一块块组合而成),等于 :第一块奇数点/2+第二块奇数点/2+.......,最后得,图的总奇数点/2

 

接着还要计算有多少块里不存在奇数点(不存在奇数点的那块中,一定没有第一种画法,只需要画一笔),累加起来就得到答案了。。。

 

 

代码:

#include <iostream>
using namespace std;

const int M = 100000 + 10;

int gree[M];
int father[M];
int rank1[M];
int save[M];
bool used[M];
bool mark[M];

void Make_Set(int x)
{
    father[x] = x;
    rank1[x] = 0;
}

int Find(int x)
{
    int k = 0;
    while (x != father[x])
    {
        save[k++] = x;
        x = father[x];
    }

    for (int j = 0; j < k; j++)
    {
        father[save[j]] = x;
    }
    return x;
}


void Union(int a, int b)
{

    int x = Find(a);
    int y = Find(b);
    
    if (x == y)
    {
        return;
    }
    if (rank1[x] < rank1[y])
    {
        father[x] = y;
    }
    else
    {
        father[y] = x;
        if (rank1[x] == rank1[y])
        {
            rank1[x]++;
        }
    }
}



int main()
{
    int n, m;
    while (~scanf("%d%d", &n, &m))
    {

        memset(gree, 0, sizeof(gree));
        memset(used, 0, sizeof(used));
        memset(mark, 0, sizeof(mark));
        while (m--)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            if (!used[a])
            {
                used[a] = 1;
                Make_Set(a);
            }
            if (!used[b])
            {
                used[b] = 1;
                Make_Set(b);
            }
            gree[a]++;
            gree[b]++;
            Union(a, b);
        }

        int ans = 0;
        for (int i = 1; i <= n; i++)
        {
            if (used[i] && gree[i] % 2 == 1)
            {    
                if (mark[Find(i)] == 0)
                {
                    mark[Find(i)] = 1;//标记该块已经有奇数点了
                }
                ans++;//计算总奇数点
            }
        }
        ans /= 2;

        //累加,看有多少块没奇数点。。。mark[老大] == 0没有奇数点
        for (int i = 1; i <= n; i++)
        {
            if (used[i] && father[i] == i && mark[i] == 0)
            {
                ans++;
            }
        }

        printf("%d\n", ans);
    }
    return 0;
}

 

 

posted on 2012-08-26 14:31  [S*I]SImMon_WCG______*  阅读(1026)  评论(0编辑  收藏  举报

导航