hdu 5438 hdu 5438 Ponds(长春网络赛 拓扑+bfs)

Ponds


Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)


Total Submission(s): 1046    Accepted Submission(s): 354



Problem Description
Betty owns a lot of ponds, some of them are connected with other ponds by pipes, and there will not be more than one pipe between two ponds. Each pond has a value v .

Now Betty wants to remove some ponds because she does not have enough money. But each time when she removes a pond, she can only remove the ponds which are connected with less than two ponds, or the pond will explode.

Note that Betty should keep removing ponds until no more ponds can be removed. After that, please help her calculate the sum of the value for each connected component consisting of a odd number of ponds
 


Input
The first line of input will contain a number T(1T30) which is the number of test cases.

For each test case, the first line contains two number separated by a blank. One is the number p(1p104) which represents the number of ponds she owns, and the other is the number m(1m105) which represents the number of pipes.

The next line contains p numbers v1,...,vp , where vi(1vi108) indicating the value of pond i .

Each of the last m lines contain two numbers a and b , which indicates that pond a and pond b are connected by a pipe.
 


Output
For each test case, output the sum of the value of all connected components consisting of odd number of ponds after removing all the ponds connected with less than two pipes.
 


Sample Input
  
  
1 7 7 1 2 3 4 5 6 7 1 4 1 5 4 5 2 3 2 6 3 6 2 7
 


Sample Output
  
  
21
 


Source
 

相对于dfs找环,我还是喜欢用bfs~所以这里给出的是bfs的方法,当然dfs也是可以的~

题目大意:度小于2的ponds就要被拆除,然后求奇数点个数的环的值和。

这里因为要找环,所以用邻接表或者是vector都是比较好用的,这里我们使用vector(感觉更加方便些)

我们先来说找度小于2的ponds的拆点过程,这里我们直接拓扑排序就能搞定~普通的拓扑排序是找度为0的点,我们这里要更改条件为0或者1的点,但是这里千万别写成《=1,因为毕竟我们要对度为0和1的点进行去除,我们的方法是标记degree【j】=-1;这样会出现死循环,我们这里对应代码详解这部分:

        for(int i=1;i<=p;i++)
        {
            int j;
            for(j=1;j<=p;j++)//寻找度为0或者是1的点
            {
                if(degree[j]==0||degree[j]==1)
                {
                    break;
                }
            }
            if(j>p)break;//假设没有了0或者1的点,拓扑完毕
            degree[j]=-1;//度标记为-1;
            for(int k=0;k<map[j].size();k++)
            {
                degree[map[j][k]]--;//对应边的点也要-1;
            }
        }

然后是如何找环:

这里要注意数据的范围,用long long 

ll bfs(int x)
{
    ll sum=0;
    int cont=1;
    vis[x]=1;
    queue<int >s;
    s.push(x);
    sum+=val[x];//当前点的处理
    while(!s.empty())
    {
        int now=s.front();
        s.pop();
        for(int k=0;k<map[now].size();k++)//对每一个相关点都要入队列。
        {
            if(vis[map[now][k]]==0&&degree[map[now][k]]>=2)//入队列的限制条件
            {
                sum+=val[map[now][k]];
                s.push(map[now][k]);
                cont++;//当前环的点的个数
                vis[map[now][k]]=1;
            }
        }
    }
    if(cont%2==1)
    {
        return sum;
    }
    else
    {
        return 0;
    }
}
//下边是主函数内代码
        for(int i=1;i<=p;i++)//每个点都要遍历
        {
            if(vis[i]==0&&degree[i]>=2)//如果当前点没有遍历过,并且度要大于2的点,进行bfs找环
            {
                output+=bfs(i);//output当然是最终解果了
            }
        }


然后是完整的AC代码:

这里注意是无向图,并且别忘了memset数组

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;
#define ll long long
vector<ll>map[10010];
ll vis[10010];
ll val[10010];
ll degree[10010];
ll bfs(int x)
{
    ll sum=0;
    int cont=1;
    vis[x]=1;
    queue<int >s;
    s.push(x);
    sum+=val[x];
    while(!s.empty())
    {
        int now=s.front();
        s.pop();
        for(int k=0;k<map[now].size();k++)
        {
            if(vis[map[now][k]]==0&&degree[map[now][k]]>=2)
            {
                sum+=val[map[now][k]];
                s.push(map[now][k]);
                cont++;
                vis[map[now][k]]=1;
            }
        }
    }
    if(cont%2==1)
    {
        return sum;
    }
    else
    {
        return 0;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll p,m;
        scanf("%I64d%I64d",&p,&m);
        memset(vis,0,sizeof(vis));
        memset(degree,0,sizeof(degree));
        for(int i=1;i<=p;i++)
        {
            map[i].clear();
        }
        for(int i=1;i<=p;i++)
        {
            scanf("%I64d",&val[i]);
        }
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            map[a].push_back(b);
            map[b].push_back(a);
            degree[a]++;
            degree[b]++;
        }
        for(int i=1;i<=p;i++)
        {
            int j;
            for(j=1;j<=p;j++)
            {
                if(degree[j]==0||degree[j]==1)
                {
                    break;
                }
            }
            if(j>p)break;
            degree[j]=-1;
            for(int k=0;k<map[j].size();k++)
            {
                degree[map[j][k]]--;
            }
        }
        ll output=0;

        for(int i=1;i<=p;i++)
        {
            if(vis[i]==0&&degree[i]>=2)
            {
                output+=bfs(i);
            }
        }
        printf("%I64d\n",output);
    }
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值