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