POJ1149 PIGS 网络最大流

19 篇文章 0 订阅
5 篇文章 0 订阅

Description

Mirko works on a pig farm that consists of M locked pig-houses and Mirko can't unlock any pighouse because he doesn't have the keys. Customers come to the farm one after another. Each of them has keys to some pig-houses and wants to buy a certain number of pigs. 
All data concerning customers planning to visit the farm on that particular day are available to Mirko early in the morning so that he can make a sales-plan in order to maximize the number of pigs sold. 
More precisely, the procedure is as following: the customer arrives, opens all pig-houses to which he has the key, Mirko sells a certain number of pigs from all the unlocked pig-houses to him, and, if Mirko wants, he can redistribute the remaining pigs across the unlocked pig-houses. 
An unlimited number of pigs can be placed in every pig-house. 
Write a program that will find the maximum number of pigs that he can sell on that day.

Input

The first line of input contains two integers M and N, 1 <= M <= 1000, 1 <= N <= 100, number of pighouses and number of customers. Pig houses are numbered from 1 to M and customers are numbered from 1 to N. 
The next line contains M integeres, for each pig-house initial number of pigs. The number of pigs in each pig-house is greater or equal to 0 and less or equal to 1000. 
The next N lines contains records about the customers in the following form ( record about the i-th customer is written in the (i+2)-th line): 
A K1 K2 ... KA B It means that this customer has key to the pig-houses marked with the numbers K1, K2, ..., KA (sorted nondecreasingly ) and that he wants to buy B pigs. Numbers A and B can be equal to 0.

Output

The first and only line of the output should contain the number of sold pigs.

Sample Input

3 3
3 1 10
2 1 2 2
2 1 3 3
1 2 6

Sample Output

7


一般增广路径方法——Ford-Fulkerson算法(标号法)

468K0MSG++1601B

#include <cstdio>
#include <cstring>
#define INF 0x3f3f3f3f

int s, t, m, n;
int c[105][105], f[105][105];
int house[1005], pre[1005];
int queue[105], qs, qe;
int mark[105], minf[105];

void init(){
	int num, k;
    memset(pre, 0, sizeof(pre));
    memset(c, 0, sizeof(c));
    scanf("%d %d", &m, &n);
    s = 0; t = n + 1;
    for(int i = 1; i <= m; i++)
    	scanf("%d", house + i);
    for(int i = 1; i <= n; i++){
        scanf("%d", &num);
        while(num--){
        	scanf("%d", &k);
        	if(!pre[k])
        		c[s][i] += house[k];
        	else
        		c[ pre[k] ][i] = INF;
        	pre[k] = i;
        }
        scanf("%d", &c[i][t]);
    }
}

void ford(){
	int v, p;
    memset(f, 0, sizeof(f));
    minf[0] = INF;

    while(true){
        for(int i = 0; i <= n + 1; i++)
        	mark[i] = -2;
        mark[0] = -1;
        
        qs = 0; queue[qs] = 0; qe = 1;
        while( qs != qe && mark[t] == -2){
        	v = queue[qs];  qs = (qs + 1) % n;
        	for(int i = 0; i <= t; i ++)
        		if( mark[i] == -2 && (p = c[v][i] - f[v][i]) ){
        			mark[i] = v; queue[qe] = i; qe = (qe + 1) % n;
        			minf[i] = minf[v] < p ? minf[v] : p;
        		}
        }
        if(mark[t] == -2) break;

        for(int i = mark[t], j = t;  i != -1;  j = i,i = mark[i] ){
        	f[i][j] += minf[t];
        	f[j][i] -= f[i][j];
        }       
    }

    int ans = 0;
    for(int i = 1; i <= n; i++)
    	ans += f[i][t];
    printf("%d\n", ans);
}

int main(){
    init();   
    ford();
    return 0;
}

最短增广路算法——SAP

396K16MSG++1673B

#include <cstdio>
#include <cstring>
#define inf 0x3f3f3f3f

int n, m, s, t;
int c[105][105], h[105], vh[105];

void init(){
	scanf("%d %d", &m, &n);
    int a[m], b[m];
    memset(a, 0, sizeof(a));
    memset(vh, 0, sizeof(vh));
    memset(h, 0, sizeof(h));
    memset(c, 0, sizeof(c));
    for(int i = 0; i < m; i++)
        scanf("%d", &b[i]);
    for(int i = 0; i < n; i++){
        int k, l, temp;
        scanf("%d", &k);
        for(int j = 0; j < k; j++){
            scanf("%d", &temp);
            if(!a[temp - 1])
                c[0][i+1] += b[temp-1];
            else 
				c[ a[temp-1] ][i+1] = inf;
            a[temp-1] = i + 1;
        }
        scanf("%d", &l);
        c[i+1][n+1] = l;
    }
}

int sap(int i, int f){
    if(i == t)
        return f;
    int old = f, minh = n - 1;
    for(int j = 0; j < n; j++){
        if(c[i][j] > 0){
            if(h[i] == h[j] + 1){
                int d = c[i][j] < old ? c[i][j] : old;
                d = sap(j, d);
                c[i][j] -= d;
                c[j][i] += d;
                old -= d;
                if(h[s] >= n)
                return  f - old;
                if(old == 0)
                    break;
            }
            if(h[j] < minh)
                minh = h[j];
        }
    }
    if(f == old){
        vh[ h[i] ]--;
        if(vh[ h[i] ] == 0)
            h[s] = n;
        h[i] = minh+1;
        vh[ h[i] ] ++;
    }
    return f - old;
}

int main(){
	init();
    s = 0, t = n + 1, vh[0] = n + 2;
    int ff = 0;
    n += 2;
    while(h[s] < n)
        ff += sap(s, inf);
    printf("%d\n", ff);
    return 0;
}

连续最短增广路算法——Dinic   

400K0MSG++2270B

#include <cstdio>
#include <cstring>
#define INF 0x3f3f3f3f

int n, m, s, t;
int house[1005], pre[1005];
int c[105][105], ps[105], dep[105];

void init(){
	int num, k;
    memset(pre, 0, sizeof(pre));
    memset(c, 0, sizeof(c));
    scanf("%d %d", &m, &n);
    s = 0; t = n + 1;
    for(int i = 1; i <= m; i++)
    	scanf("%d", house + i);
    for(int i = 1; i <= n; i++){
        scanf("%d", &num);
        while(num--){
        	scanf("%d", &k);
        	if(!pre[k])
        		c[s][i] += house[k];
        	else
        		c[ pre[k] ][i] = INF;
        	pre[k] = i;
        }
        scanf("%d", &c[i][t]);
    }
}

bool bfs(int n,int s,int t){
    int f = 0, r = 0, u, v;
    memset(dep, -1, sizeof(dep));
    ps[r++] = s;
    dep[s] = 0; //开始计算层次
    while(f < r){
        u = ps[f++];
        for(v = 0; v < n; v++)
            if(c[u][v] && dep[v] < 0){
                dep[v] = dep[u] + 1;
                ps[r++] = v;
            }
            if(u == t) break;

    }
    return dep[t] < 0; //返回能否增广
}

int Dinic(int n,int s,int t){
    int k, u, v, num, res = 0, top, tag;
    while(1){
        if( bfs(n, s, t) ) break; //不能增广,最终结果
        top = 0;
        u = s;
        while(1){
            if(u == t){  //一条增广
                ps[top++] = t;
                for(k = 0, num = INF; k < top - 1; k++)
                    if(c[ ps[k] ][ ps[k+1] ] < num){
                        tag = k; //标志最小弧的头
                        num = c[ ps[k] ][ ps[k+1] ];
                    }
                for(k=0;k<top-1;k++){
                    c[ps[k]][ps[k+1]] -= num;
                    c[ps[k+1]][ps[k]] += num;
                }
                res += num;
                top = tag; //从标志处继续找其他增广路
                u = tag;
            }
            for(v = 0; v < n; v++) 
                if(c[u][v] && dep[u] + 1 == dep[v]) break;  //从u出发存在弧u->v

            if(v<n) {ps[top++] = u; u = v;}   //压u入栈,从v出发找
            else{
                if(top == 0) break;  //确定增广路找不到
                dep[u] = -1;
                u=ps[--top]; //把u弹出,找层次图中另一条u->v
            }
        }
    }
    return res;
}

int main(){
	init();
    printf("%d\n", Dinic(n + 2, s, t));
	return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值