题意:给出一个含重边的无向联通图,以及每个点的权值,让你割掉一条边将图分成两部分使得权值和最接近,输出这两部分权值和的差,如果不存在这样的边就输出impossible。
题解:我是通过把tarjan算法改了下就过了,但是感觉对于tarjan算法的核心原理理解的还差的很多,可能思路的细节上还有问题,我就写下大致的思路,就是说再对每个点tarjan时维护一个上一个的值,这样就可以保证不在同一条边走回头路,同时为处理重边的问题,再维护一个bool性的flag,也就是说只对于第一次出现的重边不做处理,之后再出现就照常处理(也就是这俩点肯定是强连通不可割)。然后由于每个点只会访问到一次tarjan函数,所以是可以在对每个点第一次访问时进行权值相加的。最后就是判断哪条边可以割的问题,这个就不是很好理解,只有当点v可以到达的最小点low[v]大于u的当前大小时,说明接下来的图中无法到达之前访问过的点,也就是这条边可以是割边
需要注意的一点是判断割边时只可以用dfn[u]<low[v]来判断,而不可以用low[u]<low[v],原理是low[u]有可能会在之前的操作中被更新成了更小点,这一点没有想明白导致一直出错。
代码:
//无向联通图,存在重边
//割掉一条边使得两边图的权值差最小
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
const int inf=0x3f3f3f3f;
typedef vector<int>vi;
int arr[N],allsum,ans;
int n,m,dfn[N],low[N],dfn_cnt;
vi edg[N];
void tarjan(int u,int f)
{
dfn[u]=low[u]=++dfn_cnt;
bool flag=1;
for(int v:edg[u])
{
if(v==f&&flag)
{
flag=0;
continue;
}
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
arr[u]+=arr[v];
if(dfn[u]<low[v])
ans=min(ans,abs(allsum-arr[v]*2));
}
low[u]=min(low[u],low[v]);
}
}
void solve()
{
memset(dfn+1,0,sizeof(*dfn)*n);
for(int i=1; i<=n; i++)edg[i].clear();
dfn_cnt=0;
allsum=0;
ans=inf;
for(int i=1; i<=n; i++)
{
cin>>arr[i];
allsum+=arr[i];
}
while(m--)
{
int a,b;cin>>a>>b;
a++,b++;
edg[a].push_back(b);
edg[b].push_back(a);
}
tarjan(1,0);
if(ans==inf)cout<<"impossible"<<endl;
else cout<<ans<<endl;
}
int main()
{
while(cin>>n>>m)
solve();
}