BZOJ 1001: [BeiJing2006]狼抓兔子 (最小割)

虽然联赛考崩了,但还是要继续坚持下去啊,以后就去BZOJ上刷题吧QwQ

题目大意

给你一个nm的矩形平面图,斜边和直边上都有权值,选一些边,使(1,1)(n,m)分开,使这些边权值和最小!

解题思路

比较明显的一个最小割,和[ZJOI2009]狼和羊的故事很像的,都是将两个联通块分开,问分开边权和的最小值。

一开始调了比较久,答案都小了,因为我没有将从下向上走的边连上去,这种就会使答案偏小,所以以后要多注意一下这种最小割的题要连两条边。

而且这种题目好像也不用拆点。。所以一开始有点傻,拆了点做的所以GetPoint会多一个id自动忽略掉吧。。

好像最小割并不是正解,但进行一些优化能卡过去(参考了下hzwer大佬的)。

正解使S-T平面图上求最小割,具体参考这篇论文

像那种双向边,可以都连边权值大小的边,没必要连反向边了(因为可以把它们当做互为反向边)。

代码

/************************************************************** Problem: 1001 User: zjp_shadow Language: C++ Result: Accepted Time:1512 ms Memory:84972 kb ****************************************************************/ #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i) #define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } void File() { #ifdef zjp_shadow freopen ("1001.in", "r", stdin); freopen ("1001.out", "w", stdout); #endif } const int N = 1010 * 1010, M = N * 6; int to[M], Next[M], Head[N], cap[M], e = 1; inline void add_edge (int u, int v, int w) { to[++e] = v; Next[e] = Head[u]; cap[e] = w; Head[u] = e; } int S, T; int r, c; const int inf = 0x3f3f3f3f; inline int Get_Point(int x, int y, int id) { return (x - 1) * c + y; } int dis[N], n; inline bool Bfs() { Set(dis, 0); queue<int> Q; Q.push(S); dis[S] = 1; while (!Q.empty() ) { register int u = Q.front(); Q.pop(); for (register int i = Head[u]; i; i = Next[i]) if (!dis[to[i]] && cap[i]) { register int v = to[i]; dis[v] = dis[u] + 1; if (v == T) return true; Q.push(v); } } return false; } int cur[N]; int Dfs(int u, int max_flow) { if (u == T || !max_flow) return max_flow; register int flow = 0, f; for (register int& i = cur[u]; i; i = Next[i]) { if ((dis[u] + 1 == dis[to[i]]) && (f = Dfs(to[i], min(max_flow, cap[i]) ) ) ) { cap[i] -= f; cap[i ^ 1] += f; flow += f; max_flow -= f; if (!max_flow) break ; } } if (!flow) dis[u] = 0; return flow; } inline int Dinic() { int sum_flow = 0; while (Bfs() ) { For (i, 1, n) cur[i] = Head[i]; sum_flow += Dfs(S, inf); } return sum_flow; } int main () { File() ; r = read(), c = read(); For (i, 1, r) For (j, 1, c - 1) { static int u, v, w; u = Get_Point(i, j, 1); v = Get_Point(i, j + 1, 0); w = read() ; add_edge (u, v, w); add_edge (v, u, w); } For (i, 1, r - 1) For (j, 1, c) { static int u, v, w; u = Get_Point(i, j, 1); v = Get_Point(i + 1, j, 0); w = read() ; add_edge (u, v, w); add_edge (v, u, w); } For (i, 1, r - 1) For (j, 1, c - 1) { static int u, v, w; u = Get_Point(i, j, 1); v = Get_Point(i + 1, j + 1, 0); w = read() ; add_edge (u, v, w); add_edge (v, u, w); } S = Get_Point(1, 1, 0); T = Get_Point(r, c, 1); n = T; printf ("%d\n", Dinic() ) ; return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/7844257.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(288)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示