hdu2242 考研路茫茫——空调教室 (双连通)
题意:给出一些点,有值,给出一些边,然后求去掉一条边后将分成连通的两部分,且两部分的差值最小
分析:首先,求出所有的双连通分量,缩点,重新构图,再dfs (类似树形DP的过程)一遍就可以找出最小差值。但是,因为tarjan 算法的本质就是一个dfs,所有可以在求双连通分量的同时求出最小差值。
注意:有重边的情况,因为人为的加了一条双向边,那个是无效的,但是原图的重边是有效的, 在这里WA 了N次
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#define MAXN 10010
using namespace std;
int n,dfn[MAXN],low[MAXN],w[MAXN],index;
int sub,sum,num;
bool vis[MAXN];
vector<int> g[MAXN];
stack<int> st;
void tarjan(int u,int p)
{
dfn[u]=low[u]=index++;
vis[u]=true;
st.push(u);
int flag=1;//flag 用于排除人为加入的反向边,该边是无效的
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==p && flag)
{
flag=0;
continue;
}
if(!vis[v])
{
tarjan(v,u);
low[u]=min(low[v],low[u]);
if(dfn[u]<low[v])
{
int temp=0,x;
do
{
x=st.top();
temp+=w[x];
st.pop();
}while(x!=v);
sub=min(sub,abs(sum-2*temp));
w[u]+=temp;
num++;
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
int m,a,b;
while(scanf("%d %d",&n,&m)==2)
{
sum=0;
for(int i=0;i<n;i++)
{
g[i].clear();
scanf("%d",&w[i]);
sum+=w[i];
}
while(m--)
{
scanf("%d %d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
index=num=0;
memset(vis,false,sizeof(vis));
sub=INT_MAX;
while(!st.empty())
st.pop();
tarjan(0,-1);
if(num==0)
{
printf("impossible\n");
continue;
}
printf("%d\n",sub);
}
return 0;
}