重拾tarjan第一题,不过这题还要用到树形dp……所以是一道很好的综合简单题……
#include<iostream>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
template<class T> T Max(T x,T y){return x>y?x:y;}
template<class T> T Min(T x,T y){return x<y?x:y;}
#define N 10005
#define inf 999999999
stack<int> st;
vector<int> v[N];
int num,sum,sub,index,w[N],low[N],dfn[N];
bool vis[N];
void tarjan(int now, int pre)
{
int i,u,x,temp,flag;
flag=1;
low[now]=dfn[now]=index++;
vis[now]=true;
st.push(now);
for(i=0;i<v[now].size();i++)
{
u=v[now][i];
if(u==pre&&flag) //取消人为加的重边
{
flag=0;
continue;
}
if(!vis[u])
{
tarjan(u,now);
low[now]=Min(low[now],low[u]);
if(dfn[now]<low[u]) //找到一个连通分量
{
temp=0;
do
{
x=st.top();
st.pop();
temp+=w[x];
}while(x!=u);
sub=Min(sub,abs(sum-2*temp));
w[now]+=temp; //把一个连通分量缩进它的父节点去,这里实际是进行了dp
num++;
}
}
else low[now]=Min(low[now],dfn[u]);
}
}
int main()
{
//freopen("a.txt","r",stdin);
int i,m,n,x,y;
while(scanf("%d%d",&n,&m)!=EOF)
{
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",w+i);
sum+=w[i];
v[i].clear();
}
for(i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
memset(vis,false,sizeof(vis));
while(!st.empty())st.pop();
num=index=0;
sub=inf;
tarjan(0,-1);
if(num==0)printf("impossible\n"); //如果只有一个连通分量就无解
else printf("%d\n",sub);
}
return 0;
}