POJ-3436:ACM Computer Factory (Dinic最大流)

题目链接:http://poj.org/problem?id=3436

 


解题心得:

  • 题目真的是超级复杂,但解出来就是一个网络流,建图稍显复杂。其实提炼出来就是一个工厂n个加工机器,每个机器有一个效率w,q个材料入口,q个材料出口,每个口有三个数表示状态,1表示一定有入/出的材料,0表示没有入/出的材料,2表示可能有入的材料。如果一个机器入口全是0,代表这是起始机器,如果一个机器出口全是1,那么代表这是末尾机器。
  • 具体做法:
    • 将每个机器i拆成两点i和i+n分别代表进和出
    • 建立超级源点,连在起始机器上,流量INF。 建立超级汇点,找到末尾机器连在超级汇点上,流量INF。
    • 一个机器拆成的两个点i和i+n连上,流量就是这个点的效率w。
    • 然后暴力匹配,看一个点的所有出口是否可以完全对应一个点的入口,如果可以,匹配上,流量INF。
    • 跑Dinic,得到最大流。
  • 刚开始看到这个题没啥思路,因为关系太过于复杂,但是只要将题目中所有的关系提炼出来,就很容易建立一个网络图,要整体效率最大,那就是跑一个最大流啊,但是如果关系找漏GG。

 

#include <stdio.h>
#include <cstring>
#include <stdlib.h>
#include <queue>
#include <math.h>
#include <vector>
#include <climits>
using namespace std;
const int maxn = 1e4+7;
const int INF = INT_MAX;

int p, n, S, T, level[maxn], iter[maxn];
struct Machine {
    int in[15];
    int out[15];
    int p;
}m[maxn];

struct Edge {
    int to, cap, rev, flow;
    Edge(int To, int Cap, int Rev, int Flow):
            to(To), cap(Cap), rev(Rev), flow(Flow){}
};

vector <Edge> ve[maxn];


void add_edge(int s,int t, int cap) {//建边
    ve[s].push_back(Edge(t, cap, ve[t].size(), 0));
    ve[t].push_back(Edge(s, 0, ve[s].size()-1, 0));
}

void build_edge() {//找出口和入口的关系
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            if(i == j)
                continue;
            bool flag = false;
            for(int k=1;k<=p;k++) {
                if(m[j].in[k] != 2 && m[i].out[k] != m[j].in[k]) {
                    flag = true;
                    break;
                }
            }
            if(!flag)
                add_edge(i+n, j, INF);
        }
        add_edge(i, i+n, m[i].p);
    }
}


void init() {
    scanf("%d%d",&p,&n);
    S = 0, T = 2*n + 1;
    for(int i=1;i<=n;i++) {//找起始机器和末尾机器
        scanf("%d", &m[i].p);
        bool flag = false;
        for (int j = 1; j <= p; j++) {
            scanf("%d", &m[i].in[j]);
            if (m[i].in[j] == 1)
                flag = true;
        }
        if (!flag)
            add_edge(S, i, INF);
        flag = false;
        for (int j = 1; j <= p; j++) {
            scanf("%d", &m[i].out[j]);
            if (m[i].out[j] != 1)
                flag = true;
        }
        if (!flag)
            add_edge(i+n, T, INF);
    }
    build_edge();
}


bool bfs() {
    memset(level, -1, sizeof(level));
    level[S] = 0;
    queue <int> qu;
    qu.push(S);
    while(!qu.empty()) {
        int now = qu.front(); qu.pop();
        for(int i=0; i<ve[now].size(); i++) {
            Edge &e = ve[now][i];
            if(e.cap > e.flow && level[e.to] < 0) {
                level[e.to] = level[now] + 1;
                qu.push(e.to);
            }
        }
    }
    return  level[T] > 0;
}

int dfs(int now, int f) {
    if(now == T) return f;
    for(int &i=iter[now]; i<ve[now].size(); i++) {
        Edge &e = ve[now][i];
        if(e.cap > e.flow && level[e.to] > level[now]) {
            int d = dfs(e.to, min(f, e.cap-e.flow));
            if(d > 0) {
                e.flow += d;
                ve[e.to][e.rev].flow -= d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow() {//Dinic跑最大流
    int ans = 0;
    while(bfs()) {
        int f = dfs(S, INF);
        memset(iter, 0, sizeof(iter));
        if(f > 0)
            ans += f;
        else
            break;
    }
    return ans;
}

int cnt, path[maxn][5];
void Print_path(int ans) {//把路找出来
    cnt = 0;
    for(int i=n+1;i<=2*n;i++) {
        for(int j=0;j<ve[i].size();j++) {
            Edge &e = ve[i][j];
            if(e.flow > 0 && e.to <= n) {
                path[cnt][0] = i - n;
                path[cnt][1] = e.to;
                path[cnt][2] = e.flow;
                cnt++;
            }
        }
    }
    printf("%d %d\n",ans, cnt);
    for(int i=0;i<cnt;i++)
        printf("%d %d %d\n",path[i][0], path[i][1], path[i][2]);
}

int main() {
    init();
    int ans = max_flow();
    Print_path(ans);
}

 


 

posted @ 2018-08-18 16:27  GoldenFingers  阅读(430)  评论(0编辑  收藏  举报