ISAP
// UVa11248 Frequency Hopping:使用ISAP算法,加优化 // Rujia Liu struct Edge { int from, to, cap, flow; }; struct ISAP { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 int p[maxn]; // 可增广路上的上一条弧 int num[maxn]; // 距离标号计数 void AddEdge(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(t); vis[t] = 1; d[t] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]^1];///因为t本来就是最后一个结点,一般最后都是反向边的? if(!vis[e.from] && e.cap > e.flow) { vis[e.from] = 1; d[e.from] = d[x] + 1; Q.push(e.from); } } } return vis[s]; } void ClearAll(int n) { ///为了重新建边 this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void ClearFlow() { ///清除流量 for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } int Augment() { ///沿着增广边扩展flow int x = t, a = INF; while(x != s) { Edge& e = edges[p[x]]; a = min(a, e.cap-e.flow); x = edges[p[x]].from; } x = t; while(x != s) { edges[p[x]].flow += a; edges[p[x]^1].flow -= a; x = edges[p[x]].from; } return a; } int Maxflow(int s, int t) { ///求最大流 this->s = s; this->t = t; int flow = 0; BFS(); memset(num, 0, sizeof(num)); for(int i = 0; i < n; i++) num[d[i]]++; int x = s; memset(cur, 0, sizeof(cur)); while(d[s] < n) { if(x == t) { flow += Augment(); x = s; } int ok = 0; for(int i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance ok = 1; p[e.to] = G[x][i]; cur[x] = i; // 注意 x = e.to; break; } } if(!ok) { /// Retreat 重新对待。因为该节点在目前的d下已经不存在增广路了,所以我们要对他进行增广 int m = n-1; // 初值注意 for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(e.cap > e.flow) m = min(m, d[e.to]); } if(--num[d[x]] == 0) break; num[d[x] = m+1]++; cur[x] = 0; // 注意 if(x != s) x = edges[p[x]].from; } } return flow; } vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西 BFS();///重新bfs一次 vector<int> ans; for(int i = 0; i < edges.size(); i++) { Edge& e = edges[i]; if(!vis[e.from] && vis[e.to] && e.cap > 0) { ans.push_back(i); } } return ans; } void Reduce() { for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow; } void print() { printf("Graph: "); for(int i = 0; i < edges.size(); i++) printf("%d->%d, %d, %d ", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow); } }; ISAP g;
Dinic模板
lrj的代码告诉我,不能随便的在Edge里面增加一个cost,因为这样在跑起来的时候会慢100ms(在UVA上就慢了100ms,更何况其他的)
const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, flow; }; struct Dinic { int n, m, s, t; ///节点的个数,边的编号,起点,终点 vector<Edge> edges; // 边数的两倍 vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 /////////蓝书363 int inq[maxn]; // 是否在队列中 int p[maxn]; // 上一条弧 int a[maxn]; //可改进量 void ClearAll(int n) { this->n = n; ///这个赋值千万不能忘 for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void ClearFlow() { ///清除流量,例如蓝书368的UVA11248里面的优化,就有通过清除流量来减少增广次数的 for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } void Reduce() {///直接减少cap,也是减少增广次数的 for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow; } void AddEdge(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() {///bfs构建层次图 memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) {//只考虑残量网络中的弧 vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) {///a表示目前为止,所有弧的最小残量。但是也可以用它来限制最大流量,例如蓝书368LA2957,利用a来保证增量,使得最后maxflow=k if(x == t || a == 0) return a; int flow = 0, f; for(int& i = cur[x]; i < G[x].size(); i++) {//从上次考虑的弧,即已经访问过的就不需要在访问了 Edge& e = edges[G[x][i]]; if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a == 0) break;//如果不在这里终止,效率会大打折扣 } } return flow; } /**最大流*/ int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流 } return flow; } /**指定流量的最大流*/ /*这里的limit表示离上限还有多少距离 int Maxflow(int s, int t, int limit) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, limit - flow);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流 if(flow == limit) break; } return flow; } */ /**最小割*/ vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西 BFS();///重新bfs一次 vector<int> ans; for(int i = 0; i < edges.size(); i++) { Edge& e = edges[i]; if(vis[e.from] && !vis[e.to] && e.cap > 0) {///这里和ISAP不一样 ans.push_back(i); } } return ans; } void debug(){///debug for (int i = 0; i < edges.size(); i++){ printf("u = %d v = %d cap = %d flow = %d ", edges[i].from + 1, edges[i].to + 1, edges[i].cap, edges[i].flow); } } }; Dinic g;
Dinit+最小费用流模板(暂时,功能和注释需要不断加强,然后慢慢变得逐渐完整了,既可以求最大流,也可以求最小费用)
const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, flow, cost; Edge(int f = 0, int t = 0, int c = 0, int fo = 0, int cost = 0): from(f), to(t), cap(c), flow(fo), cost(cost){} }; struct Dinic { int n, m, s, t; ///节点的个数,边的编号,起点,终点 vector<Edge> edges; // 边数的两倍 vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 /////////蓝书363 int inq[maxn]; // 是否在队列中 int p[maxn]; // 上一条弧 int a[maxn]; //可改进量 void ClearAll(int n) { this->n = n; ///这个赋值千万不能忘 for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void ClearFlow() { ///清除流量,例如蓝书368的UVA11248里面的优化,就有通过清除流量来减少增广次数的 for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } void Reduce() {///直接减少cap,也是减少增广次数的 for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow; } void AddEdge(int from, int to, int cap, int cost = 0) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() {///bfs构建层次图 memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) {//只考虑残量网络中的弧 vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) {///a表示目前为止,所有弧的最小残量。但是也可以用它来限制最大流量,例如蓝书368LA2957,利用a来保证增量,使得最后maxflow=k if(x == t || a == 0) return a; int flow = 0, f; for(int& i = cur[x]; i < G[x].size(); i++) {//从上次考虑的弧,即已经访问过的就不需要在访问了 Edge& e = edges[G[x][i]]; if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a == 0) break;//如果不在这里终止,效率会大打折扣 } } return flow; } /**最大流*/ int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流 } return flow; } /**指定流量的最大流*/ /*这里的limit表示离上限还有多少距离 int Maxflow(int s, int t, int limit) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, limit - flow);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流 if(flow == limit) break; } return flow; } */ /**最小割*/ vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西 BFS();///重新bfs一次 vector<int> ans; for(int i = 0; i < edges.size(); i++) { Edge& e = edges[i]; if(vis[e.from] && !vis[e.to] && e.cap > 0) {///这里和ISAP不一样 ans.push_back(i); } } return ans; } /***************////以下是最小费用流算法 bool BellmanFord(int s, int t, int &flow, int &cost) {///当然,cost是LL,也要记得修改 this->s = s; this->t = t; for(int i = 0; i < n; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return false;///这里的条件看情况的 flow += a[t]; cost += d[t] * a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } /// 需要保证初始网络中没有负权圈(属于调用最小费用流算法) int Mincost(int s, int t) { int flow = 0, cost = 0; while(BellmanFord(s, t, flow, cost)); ///这里的flow是看情况的 return cost; } void debug(){///debug for (int i = 0; i < edges.size(); i++){ printf("u = %d v = %d cap = %d flow = %d ", edges[i].from + 1, edges[i].to + 1, edges[i].cap, edges[i].flow); } } }; Dinic g;
ISAP + 最小费用流
const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, flow, cost; Edge(int f = 0, int t = 0, int c = 0, int fo = 0, int cost = 0): from(f), to(t), cap(c), flow(fo), cost(cost){} }; struct ISAP { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 int p[maxn]; // 可增广路上的上一条弧 int num[maxn]; // 距离标号计数 /////////蓝书363 int inq[maxn]; // 是否在队列中 int a[maxn]; //可改进量 void AddEdge(int from, int to, int cap, int cost = 0) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(t); vis[t] = 1; d[t] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]^1];///因为t本来就是最后一个结点,一般最后都是反向边的? if(!vis[e.from] && e.cap > e.flow) { vis[e.from] = 1; d[e.from] = d[x] + 1; Q.push(e.from); } } } return vis[s]; } void ClearAll(int n) { ///为了重新建边 this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void ClearFlow() { ///清除流量 for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } int Augment() { ///沿着增广边扩展flow int x = t, a = INF; while(x != s) { Edge& e = edges[p[x]]; a = min(a, e.cap-e.flow); x = edges[p[x]].from; } x = t; while(x != s) { edges[p[x]].flow += a; edges[p[x]^1].flow -= a; x = edges[p[x]].from; } return a; } int Maxflow(int s, int t) { ///求最大流 this->s = s; this->t = t; int flow = 0; BFS(); memset(num, 0, sizeof(num)); for(int i = 0; i < n; i++) num[d[i]]++; int x = s; memset(cur, 0, sizeof(cur)); while(d[s] < n) { if(x == t) { flow += Augment(); x = s; } int ok = 0; for(int i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance ok = 1; p[e.to] = G[x][i]; cur[x] = i; // 注意 x = e.to; break; } } if(!ok) { /// Retreat 重新对待。因为该节点在目前的d下已经不存在增广路了,所以我们要对他进行增广 int m = n-1; // 初值注意 for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(e.cap > e.flow) m = min(m, d[e.to]); } if(--num[d[x]] == 0) break; num[d[x] = m+1]++; cur[x] = 0; // 注意 if(x != s) x = edges[p[x]].from; } } return flow; } vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西 BFS();///重新bfs一次 vector<int> ans; for(int i = 0; i < edges.size(); i++) { Edge& e = edges[i]; if(!vis[e.from] && vis[e.to] && e.cap > 0) { ans.push_back(i); } } return ans; } void Reduce() { for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow; } /***************////以下是最小费用流算法 bool BellmanFord(int s, int t, int &flow, int &cost) {///当然,cost是LL,也要记得修改 for(int i = 0; i < n; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return false;///这里的条件看情况的 flow += a[t]; cost += d[t] * a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } /// 需要保证初始网络中没有负权圈(属于调用最小费用流算法) int Mincost(int s, int t) { int flow = 0, cost = 0; while(BellmanFord(s, t, flow, cost)); ///这里的flow是看情况的 return cost; } void print() { printf("Graph: "); for(int i = 0; i < edges.size(); i++) printf("%d->%d, %d, %d ", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow); } }; ISAP g;
//**********************************************************************************************************************************************************//
接下来开始探寻用head、next的建图方式了(垃圾POJ,毁我模板TAT,T我一脸)