scu oj 4442 Party(四川省acm程序设计竞赛)

Posted jhcelue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scu oj 4442 Party(四川省acm程序设计竞赛)相关的知识,希望对你有一定的参考价值。

    

Party

n frogs are invited to a tea party. Frogs are conveniently numbered by 1,2,,n.

The tea party has black and green tea in service. Each frog has its own preference. He or she may drink only black/green tea or accept both.

There are m pairs of frogs who dislike each other. They fight when they are serving the same type of tea.

Luckily, frogs can be divided into 2 groups such that no two frogs in the same group dislike each other.

Frogs like gems. If the i-th frog can be paid wi gems instead of serving tea, it will not fight with others anymore.

The party manager has to dicide how to serve tea/pay gems to avoid fights, minimizing the total gems paid.

Input

The input consists of multiple tests. For each test:

The first line contains 2 integers n,m (1n103,0m104). The second line contains n integers w1,w2,,wn. (1wi106).

The third line contains n integers p1,p2,,pn. (1pi3). pi=1 means the i-th frog drinks only black tea. pi=2 means it drinks only green one, while pi=3means it accepts both.

Each of the following m lines contains 2 integers ai,bi, which denotes frog ai and bi dislike each other. (1ai,bin)

Output

For each test, write 1 integer which denotes the minimum total gems paid.

Sample Input

    2 1
    1 1
    3 3
    1 2
    2 1
    1 1
    2 2
    1 2
    3 2
    2 1 2
    1 3 2
    1 2
    2 3

Sample Output

  0
  1
  1


省赛过了非常久以后我才来看这道题目。

然后,yy了几个晚上。最终发现果然是神一样的建图。

题目意思:有n个青蛙喝茶,有些青蛙仅仅能喝红茶,有些青蛙仅仅能喝绿茶,有些青蛙红茶绿茶都能够喝。

如今m对青蛙之间有矛盾。有矛盾的青蛙他们不能喝一样的茶,对于每一仅仅青蛙。能够给他w[i]金币。让他不喝茶。他就不会和不论什么青蛙矛盾了。最少须要给多少金币让他们之间没有矛盾存在。这题里面另一个非常重要的信息,我一開始没有看出来。

这句话————“Luckily, frogs can be divided into 2 groups such that no two frogs in the same group dislike each other.”事实上这句话非常关键,它的意思就是这个矛盾关系图不存在奇环,是一个2分图。

 

 有了这个条件。

我们如今考虑仅仅能喝一种茶的青蛙,红和绿之间即使存在矛盾也没有影响,那么仅仅用考虑红和红,绿和绿之间的矛盾关系。如果不考虑2种都能喝的青蛙。那么红和红,绿和绿都是单独的求出最大点权独立集。

因为是2分图,能够非常easy求。

如今考虑一仅仅2种茶都能够喝的青蛙。这里是关键,我的解法是把这个青蛙拆成2个点,一个点代表他喝红茶,一个点代表他喝绿茶,首先这2个点是不能同一时候存在。那么必定有边,然后如果这仅仅青蛙和其它仅仅喝红茶的青蛙有矛盾关系。那么相应就是这个青蛙喝红茶和那个青蛙喝红茶不能同一时候存在,其它情况事实上是类似的分析。这样拆点把矛盾关系图建立出来以后,实际上还是一个2分图。如今就是选出最大点权的点让这些点之间不存在矛盾关系。

实际上就是点权最大独立集。转换成求点权最小覆盖的模型,就能非常轻松的求解了。

。。。。想这么久,果然是神建图。事实上主要还是拆点。


VIEW CODE
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int mmax  = 2010;
const int inf = 0x3fffffff;

struct node
{
    int flow;
    int en;
    int next;
}E[100*mmax];
int p[mmax];
int num;
void init()
{
    memset(p,-1,sizeof p);
    num=0;
}
void add(int st,int en,int flow)
{
    E[num].en=en;
    E[num].flow=flow;
    E[num].next=p[st];
    p[st]=num++;
    E[num].en=st;
    E[num].flow=0;
    E[num].next=p[en];
    p[en]=num++;
}

int d[mmax];
int cur[mmax];
bool vis[mmax];
bool bfs(int st,int en)
{
    memset(vis,0,sizeof vis);
    d[st]=0;
    vis[st]=1;
    queue<int>q;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=p[x]; i+1; i=E[i].next)
        {
            int v=E[i].en;
            if(!vis[v]&&E[i].flow)
            {
                vis[v]=1;
                d[v]=d[x]+1;
                q.push(v);
            }
        }
    }
    return vis[en];
}
int dfs(int st,int en,int  flow)
{
    if(st==en||flow==0)
        return flow;
    int f=0,dd;
    for(int &i=cur[st]; i+1;i=E[i].next)
    {
        int v=E[i].en;
        if(d[st]+1==d[v]&&(dd=dfs(v,en,min(flow,E[i].flow)))>0)
        {
            E[i].flow-=dd;
            E[i^1].flow+=dd;
            flow-=dd;
            f+=dd;
            if(flow==0)
                break;
        }
    }
    return f;
}
int dinic(int st,int en,int n)
{
    int flow=0;
    while(bfs(st,en))
    {
        for(int i=0;i<=n;i++)
            cur[i]=p[i];
        flow+=dfs(st,en,inf);
    }
    return flow;
}

vector<int>e[mmax];
int fg[mmax];
int w[mmax];
void dfs(int u)
{
    int SZ=e[u].size();
    for(int i=0;i<SZ;i++)
    {
        int v=e[u][i];
        if(d[v]==-1)
        {
            d[v]=d[u]^1;
            dfs(v);
        }
    }
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        for(int i=0;i<mmax;i++)
            e[i].clear();
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&fg[i]);
            if(fg[i]==3)
                sum+=w[i];
        }
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            e[u].push_back(v);
            e[v].push_back(u);
        }
        init();
        memset(d,-1,sizeof d);
        for(int i=1;i<=n;i++)
        {
            if(d[i]==-1)
            {
                d[i]=0;
                dfs(i);
            }
        }
        int st=0,en=2*n+1;
        for(int i=1;i<=n;i++)
        {
            if(d[i]==0)
            {
                if(fg[i]==1)
                    add(st,i,w[i]);
                if(fg[i]==2)
                    add(i,en,w[i]);
                if(fg[i]==3)
                {
                    add(st,i,w[i]);
                    add(i,i+n,inf);
                    add(i+n,en,w[i]);
                }
            }
            else
            {
                if(fg[i]==1)
                    add(i,en,w[i]);
                if(fg[i]==2)
                    add(st,i,w[i]);
                if(fg[i]==3)
                {
                    add(i,en,w[i]);
                    add(st,i+n,w[i]);
                    add(i+n,i,inf);
                }
            }
        }

        for(int u=1;u<=n;u++)
        {
            int Sz=e[u].size();
            for(int i=0;i<Sz;i++)
            {
                int v=e[u][i];
                if(d[u]==0 && d[v]==1)
                {
                    if( (fg[u]==fg[v]) &&   (fg[u]!=3) )
                    {
                        if(fg[u]==1)
                            add(u,v,inf);
                        else
                            add(v,u,inf);
                    }
                    else if(  (fg[u]==fg[v]) &&   (fg[u]==3)  )
                    {
                        add(u,v,inf);
                        add(v+n,u+n,inf);
                    }
                    else if(fg[u]==3 || fg[v]==3 )
                    {
                        if(fg[u]==3)
                        {
                            if(fg[v]==1)
                                add(u,v,inf);
                            else
                                add(v,u+n,inf);
                        }
                        if(fg[v]==3)
                        {
                            if(fg[u]==1)
                                add(u,v,inf);
                            else
                                add(v+n,u,inf);
                        }
                    }

                }
            }
        }
        printf("%d\n",dinic(st,en,en)-sum);
    }
    return 0;
}


以上是关于scu oj 4442 Party(四川省acm程序设计竞赛)的主要内容,如果未能解决你的问题,请参考以下文章

四川省赛 SCU - 4438

2018年第十届ACM四川省省赛题解(10 / 11)

2018 ACM 四川省赛 G. Grisaia(超棒的杜教筛好题)

SCU oj 4438:Censor

SCU - 4439 最小点覆盖

派对选址