poj 1149 最大流sap

/*
题意:一开始有m个猪栏,初始分别有一定数目的猪;有n个生意人,按先后顺序来买猪,每个生意人只能买
其中几个编号为ai的猪栏中的猪,而且此时可以重新分配这几个ai猪栏的猪;问最多能卖多少头猪。

题解:最大流,重点是建图,本题用了个极其暴力的方法建图,竟然不超时,可见数据蛮水的;
显然首先加入源点,在源点和m个猪栏分别加入有向边,边权值为初始每个猪栏的数量;然后在猪栏和商人之
间加有向边,表示猪栏ai的猪可以被商人i购买,权值为INF;加入汇点,所有商人和汇点分别加入有向边,
权值为每个商人所要买的猪的数目;求源点到汇点的最大流即为所求解。
接下来还有一个建图的重点,由于每个商人可以买的那几个猪栏的猪都可以重新分配,因此先到的商人重新
分配猪的数目可以改变后面商人可买的猪,一开始以为是在每个商人可买的猪栏之间加入双向边即可实现重
新分配猪的数量,但是这样是错误的,因为加入了双向边会导致在后面的商人买猪的时候把后面才出现的猪
栏的猪分配到前面出现的猪栏当中,这样就使得结果错误,例如:样例中1栏2栏先出现,然后才是1栏3栏,
如果加了双向边,那么3栏的10头猪就可以分配到2栏中了,但实际上是按顺序是不可以的。
再仔细分析一下可以发现其实每个生意人之间只要有一个共同的猪栏ai就可以将所能买的猪传递,前面的商
人可以将剩余的所有猪通过该猪栏ai传递给给后面可买这个栏ai的商人,因此可以在商人之间加入单向边实
现猪的传递,而且只能是先到的商人把猪传给后到的商人。
*/
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define EMAX 500000
#define VMAX 1200

const int INF = 0xfffffff;

int head[VMAX],pre[VMAX],dis[VMAX],cur[VMAX],gap[VMAX];
int EN;
struct edge
{
    int to;
    int weight;
    int next;
}e[EMAX];

void insert(int u,int v,int w) 
{
    e[EN].to = v;
    e[EN].weight = w;
    e[EN].next = head[u];    
    head[u] = EN++;
    e[EN].to = u;
    e[EN].weight = 0;
    e[EN].next = head[v];    
    head[v] = EN++;
}

int sap(int s,int t, int n)//sap求最大流模版
{
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(int i=0; i<=n; i++)
        cur[i] = head[i];
    int u = s;
    pre[s] = s;
    int ret = 0;
    int temp = -1;
    gap[0] = n;
    bool flag;
    while(dis[s] < n)
    {
        flag = false;
        for(int &i = cur[u]; i != -1; i = e[i].next)
        {
            int v = e[i].to;
            if(e[i].weight && dis[u] == dis[v] + 1)
            {
                if (temp == -1 || temp>e[i].weight)
                    temp = e[i].weight;
                pre[v] = u;
                u = v;
                if(v == t)
                {
                    ret += temp;
                    for(u = pre[u];v != s;v = u,u = pre[u])
                    {
                        e[cur[u]].weight -= temp;
                        e[cur[u]^1].weight += temp;
                    }
                    temp = -1;
                }
                flag = true;
                break;
            }
        }
        if (flag)
            continue;

        int mindis = n;
        for(int i = head[u]; i != -1 ; i = e[i].next)
        {
            int v = e[i].to;
            if(e[i].weight && mindis > dis[v])
            {
                cur[u] = i;
                mindis = dis[v];
            }
        }
        gap[dis[u]]--;
        if( gap[dis[u]] == 0)
            break;
        dis[u] = mindis+1;
        gap[dis[u]]++;
        u = pre[u];
    }
    return ret;
}

bool f[105][1001];//记录商人与猪栏之间是否相连

int main(void)
{
    int m,n,t,a,b,s[VMAX];
    while (~scanf("%d%d",&m,&n))
    {
        memset(head,-1,sizeof(head));
        memset(f,false,sizeof(f));
        EN = 0;
        for(int i=1; i<=m; i++)
        {
            scanf("%d",&t);
            insert(0,i,t);//加源点加边
        }

        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a);
            for(int j=1; j<=a; j++)
            {
                scanf("%d",&s[j]);
                insert(s[j],m+i,INF);//猪和商人间加边
                f[i][s[j]] = true;//商人i和猪栏s[j]之间有边
            }
            scanf("%d",&b);
            insert(m+i,m+n+1,b);//商人和汇点加边
        }

        //超级暴力方法在商人之间加有向边
        for(int i=1; i<n; i++)
        {
            bool temp[105];//记录是否已经加过边
            memset(temp,false,sizeof(temp));
            for(int j=1; j<=m; j++)
            {
                if (f[i][j])//判断商人i和猪栏j是否相连
                {
                    for(int k=i+1; k<=n; k++)
                        if (f[k][j] && !temp[k])//判断商人k和猪栏j是否相连
                        {
                            insert(m+i,m+k,INF);//商人i和商人k之间有公共点,加边
                            temp[k] = true;
                        }
                }
            }
        }
        printf("%d\n",sap(0,m+n+1,m+n+2));
    }
    return 0;
}

 

posted @ 2014-03-20 23:49  辛力啤  阅读(305)  评论(0编辑  收藏  举报