做法1:上下界最小流

  • 先来一发上下界最小流,思路比较暴力,就是把行和列看作n+mn+mn+m个点,(i,j)(i,j)(i,j)如果能占领就从第iii行向第jjj列连一条边,上界为1下界为0;然后从sss向每一行连边,上下界就是题目要求的范围;同理从每一列向ttt连边,上下界为题目需要的.做上下界最小流就行了.
  • 不会的去这里liu_runda的博客
  • Upd:Upd:Upd:这道题跟 BZOJ502BZOJ502BZOJ502 清理雪道 不完全一样,还需要加上从sss连出去的边的下界之和.因为第一次最大流求的是附加流的值,还需要加上下界的值才是对的

    而在 BZOJ502BZOJ502BZOJ502 清理雪道中,从sss连出去的边下界都是000(或者说没有下界),所以不用加

CODE

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template<typename T>inline void read(T &num) {
char ch; int flg=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flg=-flg;
for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
num*=flg;
}
const int MAXN = 205;
const int MAXM = 50005;
const int inf = 1e9;
struct edge { int to, nxt, c, w, C; }e[MAXM];
int n, m, k, S, T, s, t, ss, tt, sz, cnt, fir[MAXN], info[MAXN];
inline void add(int u, int v, int cc) {
e[cnt] = (edge){ v, fir[u], cc }, fir[u] = cnt++;
e[cnt] = (edge){ u, fir[v], 0 }, fir[v] = cnt++;
} int h[MAXN], gap[MAXN];
int aug(int u, int Max) {
if(u == T) return Max;
int flow = 0, delta, v;
for(int i = info[u]; ~i; i = e[i].nxt)
if(e[i].c && h[v=e[i].to]+1 == h[u]) {
delta = aug(v, min(Max-flow, e[i].c));
e[i].c -= delta, e[i^1].c += delta; info[u] = i;
if((flow+=delta) == Max || h[S] == sz) return flow;
}
if(!(--gap[h[u]])) h[S] = sz;
++gap[++h[u]]; info[u] = fir[u];
return flow;
} inline int sap() {
memset(h, 0, sizeof h);
memset(gap, 0, sizeof gap);
memcpy(info, fir, sizeof fir);
int flow = 0;
while(h[S] < sz)
flow += aug(S, inf);
return flow;
} inline void del(int u) {
for(int i = fir[u]; ~i; i = e[i].nxt) e[i].c = e[i^1].c = 0;
} int L[105], C[105], g[105][105], sumL[105], sumC[105], deg[MAXN];
int main () {
memset(fir, -1, sizeof fir);
read(n), read(m), read(k); int flow0 = 0;
for(int i = 1; i <= n; ++i) read(L[i]), flow0 += L[i]; ///!!!加上下界!!!
for(int i = 1; i <= m; ++i) read(C[i]);
int x, y;
while(k--)
read(x), read(y), g[x][y] = 1, ++sumL[x], ++sumC[y];
s = 0; t = n+m+1; ss = T+1; tt = ss+1;
for(int i = 1; i <= n; ++i) {
if(sumL[i]+L[i] > m) return puts("JIONG!"), 0;
deg[s] -= L[i], deg[i] += L[i];
if(m-sumL[i]-L[i]) add(s, i, m-sumL[i]-L[i]);
}
for(int i = 1; i <= m; ++i) {
if(sumC[i]+C[i] > n) return puts("JIONG!"), 0;
deg[n+i] -= C[i], deg[t] += C[i];
if(n-sumC[i]-C[i]) add(n+i, t, n-sumC[i]-C[i]);
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(!g[i][j]) add(i, n+j, 1);
for(int i = s; i <= t; ++i)
if(deg[i] < 0) add(i, tt, -deg[i]);
else if(deg[i] > 0) add(ss, i, deg[i]);
add(t, s, inf); //形成循环流
S = ss, T = sz = tt;
sap();
flow0 += e[cnt-1].c; //加上附加流
e[cnt-1].c = e[cnt-2].c = 0;
del(ss), del(tt); //删去超级源点和汇点
S = t, T = s, sz = T; //因为是求最小流,所以从t->s流
printf("%d\n", flow0-sap());
}

做法2:直接最大流



题解摘自<<网络流的一些建模方法 姜志豪>>

  • 这样简单多了…
  • 注意一行(一列)的士兵多于了L[i](C[i])L[i](C[i])L[i](C[i])个时,是不会被算做贡献是2的士兵的,这样就保证了答案的正确性

CODE

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template<typename T>inline void read(T &num) {
char ch; int flg=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flg=-flg;
for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
num*=flg;
}
const int MAXN = 205;
const int MAXM = 50005;
const int inf = 1e9;
struct edge { int to, nxt, c, w, C; }e[MAXM];
int n, m, k, S, T, sz, cnt, fir[MAXN], info[MAXN];
inline void add(int u, int v, int cc) {
e[cnt] = (edge){ v, fir[u], cc }, fir[u] = cnt++;
e[cnt] = (edge){ u, fir[v], 0 }, fir[v] = cnt++;
} int h[MAXN], gap[MAXN];
int aug(int u, int Max) {
if(u == T) return Max;
int flow = 0, delta, v;
for(int i = info[u]; ~i; i = e[i].nxt)
if(e[i].c && h[v=e[i].to]+1 == h[u]) {
delta = aug(v, min(Max-flow, e[i].c));
e[i].c -= delta, e[i^1].c += delta; info[u] = i;
if((flow+=delta) == Max || h[S] == sz) return flow;
}
if(!(--gap[h[u]])) h[S] = sz;
++gap[++h[u]]; info[u] = fir[u];
return flow;
} inline int sap() {
memset(h, 0, sizeof h);
memset(gap, 0, sizeof gap);
memcpy(info, fir, sizeof fir);
int flow = 0;
while(h[S] < sz)
flow += aug(S, inf);
return flow;
} int L[105], C[105], g[105][105], sumL[105], sumC[105];
int main () {
memset(fir, -1, sizeof fir);
read(n), read(m), read(k);
int sum = 0; S = 0, T = sz = n+m+1;
for(int i = 1; i <= n; ++i) read(L[i]), sum += L[i], add(S, i, L[i]);
for(int i = 1; i <= m; ++i) read(C[i]), sum += C[i], add(n+i, T, C[i]);
int x, y;
while(k--) {
read(x), read(y), g[x][y] = 1, ++sumL[x], ++sumC[y];
if(sumL[x] + L[x] > m || sumC[y] + C[y] > n) return printf("JIONG!"), 0;
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(!g[i][j]) add(i, n+j, 1);
printf("%d\n", sum-sap());
}

BZOJ 1458 / Luogu P4311 士兵占领 (上下界最小流 / 直接最大流)的更多相关文章

  1. BZOJ1458:士兵占领(有上下界最小流)

    Description 有一个M * N的棋盘,有的格子是障碍.现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵.我们称这些士兵占领了整个棋盘当满足第i行至少放 ...

  2. BZOJ 2502 清理雪道/ Luogu P4843 清理雪道 (有源汇上下界最小流)

    题意 有一个有向无环图,求最少的路径条数覆盖所有的边 分析 有源汇上下界最小流板题,直接放代码了,不会的看dalao博客:liu_runda 有点长,讲的很好,静心看一定能看懂 CODE #inclu ...

  3. bzoj 4200: [Noi2015]小园丁与老司机【dp+有上下界最小流】

    洛谷上有个点死活卡不过去,不知道是哪里写丑了orz 参考:https://www.cnblogs.com/ditoly/p/BZOJ4200.html 从上往下dp,设f为不向左右走直接上去的值,g为 ...

  4. BZOJ 3876 支线剧情 有源汇有上下界最小费用可行流

    题意: 给定一张拓扑图,每条边有边权,每次只能从第一个点出发沿着拓扑图走一条路径,求遍历所有边所需要的最小边权和 分析: 这道题乍一看,可能会想到什么最小链覆盖之类的,但是仔细一想,会发现不行,一是因 ...

  5. BZOJ 2055 80人环游世界 有上下界最小费用可行流

    题意: 现在有这么一个m人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家.    因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为1...N.假若第 ...

  6. BZOJ 2502 清理雪道(有源汇上下界最小流)

    题面 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞机, ...

  7. 【bzoj2150】部落战争 有上下界最小流

    题目描述 lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土. A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住.lanzerb把 ...

  8. sgu 176 Flow construction(有源汇的上下界最小流)

    [题目链接] http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=11025 [模型] 有源汇点的上下界最小流.即既满足上下界又满足 ...

  9. BZOJ_2502_清理雪道_有源汇上下界最小流

    BZOJ_2502_清理雪道_有源汇上下界最小流 Description        滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道), ...

随机推荐

  1. sql server 2008 数据库管理系统使用SQL语句创建登录用户步骤详解

    介绍了sql server 2008 数据库管理系统使用SQL语句创建登录用户步骤详解 --服务器角色: --固定服务器角色具有一组固定的权限,并且适用于整个服务器范围. 它们专门用于管理 SQL S ...

  2. 题解 CF437C

    基本思路---贪心 既然要求最小代价,当用一定顺序删除时代价一定最小,不难发现,每次都删去x,y中最小的,最后的总代价业一定最小! 因此就可以写出下面的简单的代码 代码 #include<ios ...

  3. 网络编程[第一篇]基于tcp协议的套接字编程

    将服务端-客户端的连接比作双方打电话的过程 2019-07-24 一.客户端 主动的一方: 客户端实例化一个socket对象--> 主动像服务端发送连接请求--> (服务端接受请求后即可进 ...

  4. chrome 调试 ios h5

    1,安装itunes, 否则无法识别iphone设备 2,开启调试模式 ,打开 iPhone 依次进入 设置 > Safari > 高级 > Web 检查 > 启用 3,下载 ...

  5. SAS学习笔记8 循环语句(do函数)

    do-end函数

  6. 如何在 arm 官网上找到合适的手册

    http://infocenter.arm.com/help/advanced/help.jsp 在这里输入合适的版号即可 这样就可以不用去 CSDN 了 100000_0000_00_EN - AR ...

  7. 使用QSaveFile类安全的读写文件(继承自QFileDevice,与QFile并列)

    QSaveFile类也是一种I/O设备,来用来读写文本文件和二进制文件,但使用该类的话,在写入操作失败时不会导致已经存在的数据丢失. 该类在执行写操作时,会先将内容写入到一个临时文件中,如果没有错误发 ...

  8. 使用paypal-php-sdk开发php国际支付

    参考:https://github.com/paypal/PayPal-PHP-SDK/wiki https://blog.csdn.net/markely/article/details/79044 ...

  9. (九)二进制文件在webservice中的处理(以DataHandler方式)

    一.需求 1. 客户端从服务端下载附件 2. 客户端上传附件到服务端 二.案例 本章通过DataHander的方式来进行传递. 注意:   1:接口中要定义@MTOM 2:方法中要使用@XmlMime ...

  10. MySQL INNER JOIN子句介绍

    MySQL INNER JOIN子句介绍 MySQL INNER JOIN子句将一个表中的行与其他表中的行进行匹配,并允许从两个表中查询包含列的行记录. INNER JOIN子句是SELECT语句的可 ...