四川省赛G.Party

题目链接:http://acm.bnu.edu.cn/v3/contest_show.php?cid=6865#problem/G
题意:n只青蛙,要么只喝绿茶,要么只喝红茶,要么两种茶都能接受,还有m个憎恶关系,互相憎恶的两只青蛙不能喝同一种茶,憎恶关系构成的图保证是一张二分图,当然,你还可以选择花费w[i]的代价删掉某只青蛙。问最少需要花费多少代价。

分析:题目问最小代价,给出的又是憎恶关系,容易想到最小割,关键是建图比较难想。首先,题目保证是二分图,可以对他黑白染色。假设没有两种茶都能喝的青蛙,那么只要对有边的且能喝茶种类相同的青蛙建边,流量连INF,跑最小割即可。现在考虑能喝两种茶的青蛙,假设他是白色的,那么可以将他拆成白红和白绿两个点,显然,白红只可能和黑红连边,白绿只可能和黑绿连边,因此我们可以考虑在左边放白绿/黑红,右侧放白红/黑绿,中间放置被拆成两个点的两种茶都能喝的青蛙,注意两种茶都能喝的青蛙之间可能有憎恶关系,我们把割边的流量设置在拆出的两个点之间,为了能让拆出两个点之间的流量起作用(选择这条边当割边就意味着删掉这只青蛙),需要从拆出的点中右侧的点连向左侧,这样就能保证删除起到了效果,具体还是要看下代码。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pi;
const int Inf=2e9,Maxn=1020*3,Maxe=320000;
int n,m,s,t,ne;
int val[Maxn],p[Maxn],col[Maxn],id[Maxn],h[Maxn];
int cur[Maxn];
vector<int>G1[Maxn];
vector<int>G[Maxn];
void dfs1(int u,int C)
{
    col[u]=C;
    for(int i=0;i<G1[u].size();i++)
    {
        int v=G1[u][i];if(col[v])continue;
        dfs1(v,3-C);
    }
}
pi e[Maxe];
void add(int u,int v,int w)
{
    e[ne]=pi(v,w);
    G[u].push_back(ne++);
    e[ne]=pi(u,0);
    G[v].push_back(ne++);
}
bool bfs()
{
    queue<int>q;
    for(int i=s;i<=t;i++)h[i]=Inf;
    h[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<G[u].size();i++)
        {
            int v=e[G[u][i]].first,w=e[G[u][i]].second;
            if(!w||h[v]!=Inf)continue;
            h[v]=h[u]+1;
            q.push(v);
        }
    }
    return h[t]!=Inf;
}
int dfs(int u,int a)
{
    if(u==t||!a)return a;
    int ret=0;
    for(int &i=cur[u];i<G[u].size();i++)
    {
        int v=e[G[u][i]].first,w=e[G[u][i]].second;
        if(h[v]!=h[u]+1||!w)continue;
        int b=dfs(v,min(a,w));
        e[G[u][i]].second-=b;
        e[G[u][i]^1].second+=b;
        ret+=b;
        a-=b;
    }
    return ret;
}
int dinic()
{
    for(int i=s;i<=t;i++)cur[i]=0;
    int ret=0;
    while(bfs())
    {
        for(int i=s;i<=t;i++)cur[i]=0;
        ret+=dfs(s,Inf);
    }
    return ret;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d",val+i);
        for(int i=1;i<=n;i++)G1[i].clear();
        for(int i=1;i<=n;i++)scanf("%d",p+i);
        for(int i=0;i<m;i++)
        {
            int u,v;scanf("%d%d",&u,&v);
            G1[u].push_back(v);
            G1[v].push_back(u);
        }
        for(int i=1;i<=n;i++)col[i]=0;
        for(int i=1;i<=n;i++)
        {
            if(!col[i])dfs1(i,1);
        }
        //for(int i=1;i<=n;i++)printf("%d ",col[i]);puts("");
        ne=s=0;
        t=n+1;
        for(int i=1;i<=n;i++)
        {
            if(p[i]==3)
            {
                id[i]=t++;
            }
        }
        for(int i=s;i<=t;i++)G[i].clear();
        if(t>=Maxn)while(1);
        for(int i=1;i<=n;i++)
        {
            if(p[i]<3)
            {
                if(col[i]==p[i])
                {
                    //printf("u=%d\n",i);
                    add(s,i,val[i]);
                    for(int j=0;j<G1[i].size();j++)
                    {
                        int v=G1[i][j];
                        if(p[v]==3)add(i,v,Inf);
                        else if(p[v]!=col[v])add(i,v,Inf);
                    }
                }
                else
                {
                    add(i,t,val[i]);
                    //if(i==1)printf("t=%d\n",t);
                    for(int j=0;j<G1[i].size();j++)
                    {
                        int v=G1[i][j];
                        if(p[v]==3)add(id[v],i,Inf);
                    }
                }
            }
            else 
            {
                add(i,id[i],val[i]);
                for(int j=0;j<G1[i].size();j++)
                {
                    int v=G1[i][j];
                    if(p[v]==3)add(id[v],i,Inf);
                }
            }
        }
        //for(int i=1;i<=n;i++)printf("%d ",col[i]);puts("");
    /*  
        for(int i=s;i<=t;i++)
        {
            printf("point%d:\n",i);
            for(int j=0;j<G[i].size();j++)
            {
                printf("to=%d w=%d\n",e[G[i][j]].first,e[G[i][j]].second);
            }
            puts("");
        }
    */  
        printf("%d\n",dinic());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值