HDU 5438 Ponds 拓扑排序(实现持续删点)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sdz20172133/article/details/86584866

Ponds
Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 2861    Accepted Submission(s): 901


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 .

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  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  which represents the number of ponds she owns, and the other is the number  which represents the number of pipes.

The next line contains  numbers , where  indicating the value of pond .

Each of the last  lines contain two numbers  and , which indicates that pond  and pond  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
2015 ACM/ICPC Asia Regional Changchun Online


题意:给你一个图,然后要求把度数小于2的点全部删去,然后问你奇数集合的点权和是多少。注意,你删去了一个点之后,可能会使得一些点又变成了度数小于2的

分析:

参考:https://www.cnblogs.com/cenariusxz/p/4806748.html

拓扑排序可以实现将度数为1的节点持续删去,具体做法为一个BFS过程,首先我们将每一个点的度数id记录,然后将所有度数为1的点入队,删去它的所有边,即对每条边连的点度数减一,度数减为1继续入队,直到队列为空,入过队列的点都进行标记,表示该点不在圈上,那么剩余没有标记的点一定要么在圈上、要么是单点或自环。这时候只要对于每个没有访问过的节点,DFS遍历它的连通区域,记录点数和权值和,如果点数为奇数,则统计权值和。但是注意如果只有一个点,不能算作一个圈,所以除了判断点数是否奇数,还需要判断点数大于1。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int SIZE = 100010;
int head[SIZE], ver[SIZE * 2], Next[SIZE * 2];
///head[i]=x表示以i起点的数组下标,ver[x]表示以i起点的终点编号
///next[x]=y下一个表示以i起点的数组下标 ,ver[y]表示另一个以i起点的终点编号

int n, m, tot;   
int id[SIZE],v[SIZE],vis[SIZE];

void add(int x, int y) {
	ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
	
}
void init(){
    memset(head,0,sizeof(head));
    tot=0;
    memset(id,0,sizeof(id));
    memset(vis,0,sizeof(vis));
}
void topo(){
    queue<int>q;
    for(int i=1;i<=n;++i)
    	if(id[i]==1)
	{
        id[i]=-1;
        q.push(i);
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=Next[i]){
            int j=ver[i];
            id[j]--;
            if(id[j]==1){
                id[j]=-1;
                q.push(j);
            }
        }
    }
}
int nnum=0;
ll sum=0;
void dfs(int x) {
	
	vis[x]=1;
    nnum++;
    sum+=v[x];
	for (int i = head[x]; i; i = Next[i]) {///遍历每一个结点
		int y = ver[i];
		 if(!vis[y]&&id[y]>0)dfs(y);
	}
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=n;++i)
        	scanf("%d",&v[i]);
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);
		    add(b,a);
		    id[b]++;//度数
		    id[a]++;
        }
        topo();
        ll ans=0;
        for(int i=1;i<=n;++i){
            if(!vis[i]&&id[i]>0)
			{
                sum=0;
                nnum=0;   ///记录结点个数
                dfs(i);
                if(nnum%2&&nnum>1)
                	ans+=sum;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sdz20172133/article/details/86584866
今日推荐