最大流. 注意有可能有多个人拥有同一个门的钥匙, 而且客人是按题目输入的顺序来的.
所以客人间要连边.
丢了个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 1000000000 struct Edge { int a, b; //边a->b int c, f; //容量c,流量f Edge *next, *back; //下一条边next,反向边back void 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, 汇t queue<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的弧,容量为c nextEdge[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); else addEdge(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 } }