[BZOJ 1001] 狼抓兔子

描述

?id=1001

分析

这是道经典的对偶图问题, 平面图最大流问题可以转化为其对偶图的最短路问题.

转化的方法就是将每个三角形区域看作是一个点, 如果两个三角形区域有公共线, 就在两个结点之间连一条权值为公共线容量的边.

关于编号问题我定义了一个id数组. 表示以点 (x(0~n-2), y(0~m-2)) 为左上角的三角形区域的编号. 右上三角的编号为id[x][y], 右下为id[x][y]^1.

对偶图问题还是不太懂. 一开始我认为应该建立无向图, MLE多次后改为有向图. 但两个三角之间是竖向边的情况该从谁向谁连呢? 两种都试了, 发现竟然都能AC, 但是一个7296ms一个1752ms. 求解释. 后来发现不和起点或终点相连的竖边根本不用考虑, 连都不用连就过了. 而且91196kb, 1404ms. 数据的问题吗? 无语~

代码

139808kb 1752ms

;const int INF = 1e9 + 7;const int maxn = 2500000 + 10;const int maxm = 5000 + 10;struct Edge {int from, to, dist;};vector<Edge> edges;vector<int> G[maxn];void AddEdge(int from, int to, int dist) {edges.push_back((Edge){from, to, dist});G[from].push_back(edges.size()-1);}int s, t, d[maxn];queue<int> Q;bool inq[maxn];int SPFA() {for(int i = 0; i <= t; i++) d[i] = INF;Q.push(s); inq[s] = 1; d[s] = 0;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(d[e.to] > d[u] + e.dist) {d[e.to] = d[u] + e.dist;if(!inq[e.to]) {Q.push(e.to);inq[e.to] = 1;}}}}return d[t];}int id[maxm][maxm]; int main() {int n, m;scanf(“%d%d”, &n, &m);// special judgeif(n == 1 || m == 1) {int minv = INF;for(int i = 0; i < max(n, m)-1; i++) {int v;scanf(“%d”, &v);minv = min(minv, v);}printf(“%d\n”, minv);return 0;}// 根据左上角的点的编号(x(0~n-2), y(0~m-2))计算格子的编号 int c = 0;for(int x = 0; x < n-1; x++) {for(int y = 0; y < m-1; y++)id[x][y] = c, c += 2;}s = c; t = s^1;// 横向道路for(int x = 0; x < n; x++)for(int y = 0; y < m-1; y++) {int v;scanf(“%d”, &v);if(x == 0) AddEdge(s, id[x][y], v);else if(x == n-1) AddEdge(id[x-1][y]^1, t, v);else AddEdge(id[x-1][y]^1, id[x][y], v);}// 纵向道路 for(int x = 0; x < n-1; x++)for(int y = 0; y < m; y++) {int v;scanf(“%d”, &v);if(y == 0) AddEdge(id[x][y]^1, t, v);else if(y == m-1) AddEdge(s, id[x][y-1], v);else AddEdge(id[x][y-1], id[x][y]^1, v); // 连法不同, 效率相差很大!其实可以不连…}// 斜向道路 for(int x = 0; x < n-1; x++)for(int y = 0; y < m-1; y++) {int v;scanf(“%d”, &v);AddEdge(id[x][y], id[x][y]^1, v);}printf(“%d\n”, SPFA());return 0;}

还有个网络流写法(MLE) 权当娱乐…

;const int INF = 1e9 + 7;const int maxn = 1000000 + 10;struct Edge {int from, to, cap, flow;};struct Dinic { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; bool vis[maxn]; int d[maxn], cur[maxn];// 其实如果都是无向边, 反向边的容量可以直接设为cap, 就不用加两次边了 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(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) {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);}return flow; }}dinic;int n, m;inline int encode(int x, int y) {return x * m + y + 1;}int main() {scanf(“%d%d”, &n, &m);int cap;for(int x = 0; x < n; x++)for(int y = 0; y < m-1; y++) {scanf(“%d”, &cap);dinic.AddEdge(encode(x,y), encode(x, y+1), cap);dinic.AddEdge(encode(x, y+1), encode(x,y), cap); // }for(int x = 0; x < n-1; x++)for(int y = 0; y < m; y++) {scanf(“%d”, &cap);dinic.AddEdge(encode(x,y), encode(x+1, y), cap);dinic.AddEdge(encode(x+1, y), encode(x,y), cap); // }for(int x = 0; x < n-1; x++)for(int y = 0; y < m-1; y++) {scanf(“%d”, &cap);dinic.AddEdge(encode(x,y), encode(x+1, y+1), cap);dinic.AddEdge(encode(x+1, y+1), encode(x,y), cap); // }printf(“%d\n”, dinic.Maxflow(1, m*n));return 0;}

,躲在某一时间想念一段时光的掌纹,

[BZOJ 1001] 狼抓兔子

相关文章:

你感兴趣的文章:

标签云: