解题报告 之 POJ3463 ACM Computer Factory
Description
As you know, all the computers used for ACM contests must be identical, so the participants compete on equal terms. That is why all these computers are historically produced at the same factory.
Every ACM computer consists of P parts. When all these parts are present, the computer is ready and can be shipped to one of the numerous ACM contests.
Computer manufacturing is fully automated by using N various machines. Each machine removes some parts from a half-finished computer and adds some new parts (removing of parts is sometimes necessary as the parts cannot be added to a computer in
arbitrary order). Each machine is described by its performance (measured in computers per hour), input and output specification.
Input specification describes which parts must be present in a half-finished computer for the machine to be able to operate on it. The specification is a set of P numbers 0, 1 or 2 (one number for each part), where 0 means that corresponding part
must not be present, 1 — the part is required, 2 — presence of the part doesn‘t matter.
Output specification describes the result of the operation, and is a set of P numbers 0 or 1, where 0 means that the part is absent, 1 — the part is present.
The machines are connected by very fast production lines so that delivery time is negligibly small compared to production time.
After many years of operation the overall performance of the ACM Computer Factory became insufficient for satisfying the growing contest needs. That is why ACM directorate decided to upgrade the factory.
As different machines were installed in different time periods, they were often not optimally connected to the existing factory machines. It was noted that the easiest way to upgrade the factory is to rearrange production lines. ACM directorate decided to
entrust you with solving this problem.
Input
Input file contains integers PN, then N descriptions of the machines. The description of ith machine is represented as by 2 P + 1 integers QiSi,1Si,2...Si,PDi,1Di,2...Di,P,
where Qi specifies performance, Si,j — input specification for part j, Di,k — output specification for part k.
Constraints
1 ≤ P ≤ 10, 1 ≤ N ≤ 50, 1 ≤ Qi ≤ 10000
Output
Output the maximum possible overall performance, then M — number of connections that must be made, then M descriptions of the connections. Each connection between machines A and B must be described by three positive numbers ABW,
where W is the number of computers delivered from A to B per hour.
If several solutions exist, output any of them.
Sample Input
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
3 5 5 0 0 0 0 1 0 100 0 1 0 1 0 1 3 0 1 0 1 1 0 1 1 0 1 1 1 0 300 1 1 2 1 1 1
2 2 100 0 0 1 0 200 0 1 1 1
Sample Output
25 2 1 3 15
2 3 10 4 5 1 3 3 3 5 3 1 2 1 2 4 1 4 5 1
0 0
题目大意:现在你要组装电脑,一台电脑由p个组件构成,现在有n台组装机。每一台组装机接收一定格式的半成品(输入中会给出组装机i的输入需要满足的格式,对于组件j,1表示该组件要有,0表示该组件必须没有,2表示该组件可有可无),输出组装后另一种格式的半成品(或成品,即全部为1)。每台组装机有一个组装能力cap。问你最多能组装多少台电脑?并且每台组装机相互之间是传递了多少半成品。
分析:凭借着我高超的阅读理解能力以及强大的网络流功底(怎么我自己都不相信?!)。一看就明显是最大流,而且思路都很清晰啊,拆点,超级源点汇点,以及残余网络流量判定。居然在一个细节被坑了三个小时。。
好吧说正题:怎么构造最大流呢?首先所有组装机拆点,负载为组装机的cap,以此限制流量。然后如果该组装机要求的输入格式为全0(即接受还未开始组装的电脑),那么超级源点与该组装机相连,负载为INF;如果组装机输出格式为全1,则该组装机与超级汇点相连,负载为INF。然后遍历任意两台组装机i、j,如果out[i]满足in[j]
---> 即第i台组装机的输出格式满足第j台组装机的输入格式,那么就连一条边,负载为INF。至此跑最大流看看是多少即可。对于残余网络,你去扫描所有从组装机入点(拆点后的入点)所连接的所有边,筛选出既不是连接des,也不是连接拆点的出点的那些边,输出即可。
如果看到这里你都觉得没问题的话:那么你就跟我犯了一样的错误。那么错误在哪呢? 源点与组装机连接的条件并不要求组装机输入格式为全0,还可以有2!(仔细想想)。所以判断的时候应该统一用match函数(见代码)而非使用in[i]==string(p,‘0‘)这种炫技作死的手法(p表示零件数)。
BTW,似乎也有不拆点的方法,但是数据量很小所以拆不拆其实差不多。
上代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<string> using namespace std; const int MAXM = 251000; const int MAXN = 510; const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, next, oricap; }; Edge edge[MAXM]; int output[MAXM]; int level[MAXN]; int head[MAXN]; int cap[MAXN]; string in[MAXN]; string out[MAXN]; int src, des, cnt; bool match( string out, string in, int p ) { for (int i = 0; i < p; i++) if (out[i] + in[i] == '0' + '1') return false; return true; } void addedge( int from, int to, int cap ) { edge[cnt].from = from; edge[cnt].to = to; edge[cnt].cap = edge[cnt].oricap = cap; edge[cnt].next = head[from]; head[from] = cnt++; swap( from, to ); edge[cnt].from = from; edge[cnt].to = to; edge[cnt].cap = edge[cnt].oricap = 0; edge[cnt].next = head[from]; head[from] = cnt++; } int bfs( ) { memset( level, -1, sizeof level ); queue<int>q; while (!q.empty( )) q.pop( ); level[src] = 0; q.push( src ); while (!q.empty( )) { int u = q.front( ); q.pop( ); for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (edge[i].cap > 0 && level[v] == -1) { level[v] = level[u] + 1; q.push( v ); } } } return level[des] != -1; } int dfs( int u, int f ) { if (u == des) return f; int tem; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (edge[i].cap>0 && level[v] == level[u] + 1) { tem = dfs( v, min( f, edge[i].cap ) ); if (tem > 0) { edge[i].cap -= tem; edge[i ^ 1].cap += tem; return tem; } } } level[u] = -1; return 0; } int Dinic( ) { int ans = 0, tem; while (bfs( )) { while ((tem = dfs( src, INF )) > 0) { ans += tem; } } return ans; } int main( ) { int p, n; src = 0; des = 505; while (cin >> p >> n) { memset( head, -1, sizeof head ); cnt = 0; for (int i = 1; i <= n; i++) { cin >> cap[i]; in[i] = out[i] = ""; char ch; for (int j = 1; j <= p; j++) { cin >> ch; in[i] += ch; } for (int j = 1; j <= p; j++) { cin >> ch; out[i] += ch; } } for (int i = 1; i <= n; i++) { addedge( i, i + 50, cap[i] ); if (match(in[i],string(p,'0'),p)) addedge( src, i, INF ); if (match(out[i],string(p,'1'),p)) addedge( i + 50, des, INF ); for (int j = 1; j <= n; j++) if (match( out[i], in[j], p )) addedge( i + 50, j, INF ); } int ans = Dinic( ); int num = 0; for (int i = 1 + 50; i <= n + 50; i++) { for (int j = head[i]; j != -1; j = edge[j].next) { if (edge[j].from == edge[j].to + 50 || edge[j].to == des) continue; if (edge[j].oricap > edge[j].cap) output[num++] = j; } } printf( "%d %d\n", ans, num ); for (int i = 0; i < num; i++) { printf( "%d %d %d\n", edge[output[i]].from - 50, edge[output[i]].to, edge[output[i]].oricap - edge[output[i]].cap ); } } return 0; }
好了我要去赶MATLAB的作业了。。尼玛这个细节我终身难忘。再次证明了构图是最大流最难的部分!!