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); }