hdu 2242 考研路茫茫――空调教室(Tarjan+树型DP)

27 篇文章 0 订阅

题意:给你N个点,M条无向边,并且每个点有一个权值,问把哪条边去掉,能使图分成两个部分,并且这两个部分的权值差最小。

跑一次Tarjan转化成一棵树,然后跑一次树型DP(特别注意重边的处理)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int N=10005;
struct Edge
{
    int v;
    Edge* nxt;
}memo[N*10],*cur,*org[N],*adj[N];

int n,m,mi,sum,valu[N],cnt[N];
int dfn[N],low[N],id[N],idx,scnt;
bool insta[N],vis[N];
stack<int> st;

int iabs(int x){return x>0?x:-x;}
void dfs(int u,int fa)
{
    vis[u]=1;
    for(Edge* it=adj[u];it;it=it->nxt)
    {
        int v=it->v;
        if(v==fa||vis[v]) continue;
        dfs(v,u);
        cnt[u]+=cnt[v];
    }
    mi=min(mi,iabs(sum-2*cnt[u]));
}
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=idx++;
    st.push(u); insta[u]=1;
    int v,flag=1;
    for(Edge* it=org[u];it;it=it->nxt)
    {
        v=it->v;
        if(v==fa&&flag){flag=0;continue;}
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(insta[v]&&dfn[v]<low[u]) low[u]=dfn[v];
    }
    if(low[u]==dfn[u])
    {
        while(1)
        {
            int top=st.top(); st.pop();
            insta[top]=0;
            id[top]=scnt;
            if(top==u) break;
        }
        scnt++;
    }
}
void addEdge(int u,int v,Edge* head[])
{
    cur->v=v;
    cur->nxt=head[u];
    head[u]=cur++;
}
void init()
{
    cur=memo;
    idx=1,scnt=1,sum=0,mi=(1<<30);
    memset(adj,0,sizeof(adj));
    memset(org,0,sizeof(org));
    memset(dfn,0,sizeof(dfn));
    memset(insta,0,sizeof(insta));
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();

        int u,v;
        for(int i=0;i<n;i++) scanf("%d",&valu[i]);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            addEdge(u,v,org);
            addEdge(v,u,org);
        }
        for(int i=0;i<n;i++) if(!dfn[i]) tarjan(i,-1);
        if(scnt<=2) puts("impossible");
        else
        {
            for(int i=0;i<n;i++)
            {
                cnt[id[i]]+=valu[i]; sum+=valu[i];
                for(Edge* it=org[i];it;it=it->nxt)
                {
                    u=id[i],v=id[it->v];
                    if(u!=v) addEdge(u,v,adj),addEdge(v,u,adj);
                }
            }
            dfs(1,-1);
            printf("%d\n",mi);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值