POJ - 3436 ACM Computer Factory (最大流 +拆点)

http://poj.org/problem?id=3436

题意:

ACM需要电脑,现在有N个工厂,P种生产类型,0是不能有的,1是必须的,2是可有可无的,每个工厂需要P种原料而且会生产P种产品,问ACM最多可以获得多少电脑

思路:

很明显的一个最大流,只是没有明确的源点和汇点,这是要自己建立一个超级源点和超级汇点,超级源点S与原料需求为0的相连,超级汇点T与P种产品都能生产的工厂相连,而工厂之间则按照自己的需求来互相连接。
因为每个工厂都有原料需求和产品产出,所以每个工厂要进行拆点处理,1-n表示工厂的原料, n + 1 -> 2 * n表示工厂的产品产出,注意工厂与工厂建边的时候必须要是尾巴与头建边,这样才是首尾相连
最后输出的话,对于每一个边如果它额反边的w大于0,就说明这条边最大流流过,所以可以把它直接输出出去
我是直接暴力找种类数,其实有很多巧妙的方法,你们自己去想把
第一个样例的图
3 4
15 0 0 0 0 1 0
10 0 0 0 0 1 1
30 0 1 2 1 1 1
3 0 2 1 1 1 1

这里写图片描述

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <string.h>
#include <queue>
#include <stack>
#include <deque>
#include <stdlib.h>
#include <bitset>

using namespace std;

#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 25000
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1e9 + 7

struct Func{
    int p[15], d[15];
    int w;
}func[maxn];

struct Edge{
    int v, w, nxt;
}edge[maxn];

struct PPre {
    int pre, w;
};

int tot, p, n, dis[maxn], head[maxn], aug[maxn], cur[maxn], flag[maxn], pre[maxn], gap[maxn];
int pp[maxn];;
bool vis = 0;
bool vvis[maxn];
int st = 0, en, cnt = 0;
vector<int> vv[maxn];
void addEdge(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot ++;
}
/*
bool bfs() {
    memset(dis, -1, sizeof(dis));
    dis[en] = 0;
    queue<int> que;
    que.push(en);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for (int i = head[u]; i + 1; i = edge[i].nxt) {
            if(dis[edge[i].v] == -1 && edge[i ^ 1].w > 0) {
                dis[edge[i].v] = dis[u] + 1;
                que.push(edge[i].v);
            }
        }
    }
    return dis[st] != -1;
}

int dfs(int u, int flow) {
    if(u == en) return flow;
    int belte = flow;
    for (int i = head[u]; i + 1; i = edge[i].nxt) {
        if(dis[u] == dis[edge[i].v] + 1 && edge[i].w) {
            int d = dfs(edge[i].v, min(belte, edge[i].w));
            edge[i].w -= d; edge[i ^ 1].w += d;
            belte -= d;
            if(belte == 0) break;
        }
    }
    return flow - belte;
}


int dinic() {
    int ans = 0;
    tot = 0;
    while(bfs()) {
        ans += dfs(0, INF);
    }
    return ans;
}
*/
int SAP(int n){
    int max_flow = 0,u = st, v;
    int id, mindep;
    aug[st] = INF;
    pre[st] = -1;//S的父节点为-1S
    memset(dis, 0, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    gap[0] = n;//初始位置为0的有n个
    for(int i = 0; i <= n; i ++)
        cur[i] = head[i]; // 初始化当前弧为第一条弧
    while(dis[st] < n){ //当dis[S]< n时
        int flag = 0;
        if(u == en){
            max_flow += aug[en];
            for(v= pre[en]; v != -1; v = pre[v]){     // 路径回溯更新残留网络
                id = cur[v];//v是T的前继,id是前继在edge里的编号
                edge[id].w -= aug[en];
                edge[id^1].w += aug[en];
                aug[v] -= aug[en];   // 修改可增广量,以后会用到
                if(edge[id].w == 0) // 不回退到源点,仅回退到容量为0的弧的弧尾
                    u = v;
            }
        }
        for(int i = cur[u]; i != -1; i = edge[i].nxt){
            v = edge[i].v;    // 从当前弧开始查找允许弧
            if(edge[i].w > 0 && dis[u] == dis[v] + 1){  // 找到允许弧
                flag = 1;
                pre[v] = u;
                cur[u] = i;//记录u这个点的边的编号
                aug[v] = min(aug[u], edge[i].w);//更新aug[v]
                u = v;//u向下
                break;
            }
        }
        if(!flag){//没找到弧
            if(--gap[dis[u]] == 0)    /* gap优化,层次树出现断层则结束算法 */
                break;
            mindep = n;
            cur[u] = head[u];
            for(int i = head[u]; i != -1; i = edge[i].nxt){
                v = edge[i].v;
                if(edge[i].w > 0 && dis[v] < mindep){
                    mindep = dis[v];
                    cur[u] = i;   // 修改标号的同时修改当前弧
                }
            }
            dis[u] = mindep + 1;
            gap[dis[u]] ++;
            if(u != st)  // 回溯继续寻找允许弧
                u = pre[u];
        }
    }
    return max_flow;
}


void solve() {
    int u;
    int ans = SAP(en + 1);
    int num = 0;
    //直接暴力找种类数
    for (u = n + 1; u < 2 * n + 1; u ++) {
        for (int i = head[u]; i + 1; i = edge[i].nxt) {
            if(edge[i].v > 0 && edge[i].v <= n && edge[i ^ 1].w && u - n != edge[i].v) {
                num ++;
            }
        }
    }
    printf ("%d %d\n", ans , num);
    for (u = n + 1; u < 2 * n + 1; u ++) {//从n+1开始遍历,因为是上一个的尾巴与下一个头相连
        for (int i = head[u]; i + 1; i = edge[i].nxt) {//遍历这个点的所以边
            if(edge[i].v > 0 && edge[i].v <= n && edge[i ^ 1].w && u - n != edge[i].v) {//要注意u-n不能与 edge[i].v 相同
                printf ("%d %d %d\n", u - n, edge[i].v, edge[i ^ 1].w);
            }
        }
    }
}

int main(int argc, const char * argv[]) {
    scanf("%d %d", &p, &n);
    tot = 0;
    en = 2 * n + 1;
    memset(head, -1, sizeof(head));
    for (int i = 1; i <= n; i ++) {
        scanf("%d", &func[i].w);
        vis = 0;
        for (int j = 1; j <= p; j ++) {
            scanf("%d", &func[i].p[j]);
            if(func[i].p[j] == 1)
                vis = 1;
        }
        if(!vis) {//与源点建边,权值为INF
            addEdge(st, i, INF);
            addEdge(i, st, 0);
            vvis[i] = 1;
        }
        vis = 0;
        for (int j = 1; j <= p; j ++) {
            scanf("%d", &func[i].d[j]);
            if(func[i].d[j] != 1)
                vis = 1;
        }
        if(!vis) {//与汇点建边权值为INF
            addEdge(n + i, en, INF);
            addEdge(en, n + i, 0);
            vvis[i] = 1;
        }
    }
    for (int i = 1; i <= n; i ++) {
        addEdge(i, n + i, func[i].w);
        addEdge(n + i, i, 0);
        for (int j = 1; j <= n; j ++) { // i的原料由j提供 j -> i
            if(i == j) continue;
            vis = 0;
            for (int k = 1; k <= p; k ++) {
                if(func[i].p[k] != func[j].d[k] && func[i].p[k] != 2) {
                    vis = 1;
                    break;
                }
            }
            if(!vis) {
                addEdge(n + j, i, INF);//与可建边的点建边权值为INF
                addEdge(i, n + j, 0);
            }
        }
    }
    solve();
    return 0;
}


猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/81975778