题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5438
题意:有一些带权的池塘,池塘间用管道连接,要求去掉剩下池塘中度数<=1的池塘,求剩下的池塘中卫奇数图的权值和
题解:先利用拓扑方法,利用栈,将所有度数<=1的点进栈,每次对出栈的池塘遍历连接的池塘度数-1,将新产生的度数<=1的进栈,直至去完所有满足的池塘,用vis数组记录去掉的点,然后同过dfs遍历,每次遍历时记录遍历的池塘数和权值,若满足为奇数就进行加,在遍历时要通过标记vis数组来记录该值已经被遍历,防止重复计数,最后得出答案
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
int value[10050];//记录权值
typedef struct
{
int e,next;
}Edge;
Edge edge[200500];//记录边
int head[10050];//记录以该点位头的边
int du[10050];//记录度数
bool vis[10050];//记录是否已经去掉该点和是否遍历过该点
long long sum;
void addEdge(int s,int e,int i)//加边
{
edge[i].e=e;
edge[i].next=head[s];
head[s]=i;
}
void dfs(int root,int& num)
{
sum+=value[root];
vis[root]=1;
int nextroot=head[root];
while(nextroot!=-1)
{
if(vis[edge[nextroot].e]) {nextroot=edge[nextroot].next;continue;}
num++;
dfs(edge[nextroot].e,num);
nextroot=edge[nextroot].next;
}
}
int main()
{
int t,n,e;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&e);
for(int i=1;i<=n;i++)
{
scanf("%d",&value[i]);
}
memset(du,0,sizeof(du));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=0;i<e;i++)
{
int s,e;
scanf("%d %d",&s,&e);
addEdge(s,e,2*i);
addEdge(e,s,2*i+1);
du[s]++;
du[e]++;
}
queue<int> que;
for(int i=1;i<=n;i++)
{
if(du[i]<=1){que.push(i);vis[i]=1;}
}
while(!que.empty())//拓扑排序
{
int now=que.front();que.pop();
if(du[now]==1)
{
int next=head[now];
while(next!=-1)
{
int ed=edge[next].e;
du[ed]--;
if(du[ed]<=1 && !vis[ed]){que.push(ed);vis[ed]=1;}
next=edge[next].next;
}
head[now]=-1;
}
}
long long ans=0;
for(int i=1;i<=n;i++)//dfs
{
if(vis[i]) continue;
sum=0;
int d=1;
dfs(i,d);
if(d%2) ans+=sum;
}
printf("%lld\n",ans);
}
return 0;
}