POJ 1149 PIGS

来源:互联网 发布:车载音乐视频软件 编辑:程序博客网 时间:2024/05/17 04:54

最大流. 注意有可能有多个人拥有同一个门的钥匙, 而且客人是按题目输入的顺序来的.

所以客人间要连边.

丢了个sap模板


代码:

#include <string>#include <cmath>#include <queue>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;//调用方法://init()初始化,addEdge()增加边,maxFlow()求最大流#define MAXN 1110 //顶点个数#define MAXM MAXN*MAXN //边的条数#define INF 1000000000struct Edge {int a, b; //边a->bint c, f; //容量c,流量fEdge *next, *back; //下一条边next,反向边backvoid setEdge(int a, int b, int c, Edge *next) {this->a = a;this->b = b;this->c = c;this->next = next;this->back = NULL;this->f = 0;}} *edge[MAXN], nextEdge[MAXM];int dist[MAXN]; //距离标号int edgeNum = 0; //己经使用的边的个数int counts[MAXN]; //各标号个数,出现断屋即无增广路void init(int n) {for(int i = 0; i < n; ++i) {///0~n-1??edge[i] = NULL;counts[i] = 0;}edgeNum = 0;}void init_label(int n, int s, int t) {//顶点个数n,源s, 汇tqueue<int> que;que.push(t);memset(dist, -1, sizeof(dist));//for(int i = 0; i < MAXN; ++i) {// dist[i] = -1;//}dist[t] = 0;++counts[dist[t]];while(!que.empty()) {int now = que.front();que.pop();for(Edge *next = edge[now]; next != NULL; next = next->next) {if(next->f != 0) continue;int b = next->b;if(dist[b] == -1) {dist[b] = dist[now] + 1;++counts[dist[b]];que.push(b);}}}}void addEdge(int x, int y, int c) {//增加一条x->y的弧,容量为cnextEdge[edgeNum].setEdge(x, y, c, edge[x]);nextEdge[edgeNum + 1].setEdge(y, x, 0, edge[y]);edge[x] = &nextEdge[edgeNum];edge[y] = &nextEdge[edgeNum + 1];edge[x]->back = edge[y];edge[y]->back = edge[x];edgeNum += 2;}int maxFlow(int n, int s, int t) {int ret = 0;init_label(n, s, t);Edge *path[MAXN]; //如果MAXN很大,可以开全局数组Edge *current[MAXN]; //如果MAXN很大,可以开全局数组memcpy(current, edge, sizeof(edge));int path_n = 0; //路径长度int i = s;while(1) {if(i == t) { //找到增广路int minFlow = INF, minK; //最小流和瓶颈位置值for(int k = 0; k < path_n; ++k) {if(path[k]->c < minFlow) {minFlow = path[k]->c;minK = k;}}ret += minFlow;for(int k = 0; k < path_n; ++k) {path[k]->c -= minFlow;path[k]->back->c += minFlow;path[k]->f += minFlow;path[k]->back->f = -(path[k]->f);}path_n = minK;i = path[path_n]->a;}if(dist[i] != 0 && counts[dist[i] - 1] == 0) break;Edge *next;for(next = current[i]; next != NULL; next = next->next) {if(next->c == 0) continue;int y = next->b;if(dist[i] == dist[y] + 1) {break;}}if(next != NULL) { //next是一张允许弧current[i] = next;path[path_n++] = next;i = next->b;} else { //无允许弧,修改标号int minLabel = n;for(Edge * next = edge[i]; next != NULL; next = next->next) {if(next->c == 0) continue;int y = next->b;if(dist[y] < minLabel) {minLabel = dist[y];current[i] = next; //最小标号就是最新的允许弧}}--counts[dist[i]];dist[i] = minLabel + 1;++counts[dist[i]];if(i != s) { //路径改变,顶点肯定不是允许弧--path_n;i = path[path_n]->a;} else if(dist[i] > n){ //没有增广路return ret;}}}return ret;}// 顾客在买完猪便将门锁上,而且顾客是按照顺序来的。int pre[1002];int main(){int tn, tm;while(scanf("%d%d", &tm, &tn)!=EOF){memset(pre, -1, sizeof(pre));init(tm+tn+2);for(int i=0; i<tm; i++){int tt;scanf("%d", &tt);addEdge(tm+tn, i, tt);}for(int i=0; i<tn; i++){int ta; scanf("%d", &ta);while(ta--){int tk; scanf("%d", &tk);if(pre[tk]==-1)addEdge(tk-1, i+tm, INF);elseaddEdge(pre[tk], i+tm, INF);pre[tk] = i+tm;}int tb; scanf("%d", &tb);addEdge(i+tm, tm+tn+1, tb);}printf("%d\n", maxFlow(tm+tn+2, tm+tn, tm+tn+1));//0-th}}


原创粉丝点击