留坑待填
网络流做题技巧:
1、板子很熟的情况下肉眼差错,放弃单步调试。
2、反复检查自己的网络流模型的正确性,确保正确才开始写。
3、手动构造多组数据卡自己的程序,网络流的题目通常样例很垃圾而且对拍不好写。
通用模板
最大流模板:
#include
#include
#include
#include
#include
#define N 10050
#define INF 1<<30
using namespace std;
struct Node{int u,cap,rec; };
vector e[N];
int p[N],S,T;
void add_Node(int a,int b,int v) {
Node tmp;
tmp.u = b;
tmp.cap = v;
tmp.rec = e[b].size();
e[a].push_back(tmp);
tmp.u = a;
tmp.cap = 0;
tmp.rec = e[a].size() - 1;
e[b].push_back(tmp);
return ;
}
bool BFS() {
bool flag = false;
memset(p,0,sizeof(p));
queue q;
p[S] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
if (u == T) flag = true;
for (int i=0;i 0 && p[v] == 0) {
p[v] = p[u] + 1;
q.push(v);
}
}
}
return flag;
}
int DFS(int u,int flow) {
if (u == T) return flow;
int f = flow , g = 0;
for (int i=0;i 0 && p[v] == p[u] + 1 && (tmp = DFS(v,min(f,cp))) > 0) {
f -= tmp;
g += tmp;
e[u][i].cap -= tmp;
int rc = e[u][i].rec;
e[v][rc].cap += tmp;
}
}
return g;
}
void build() {
}
int main()
{
build();
int max_flow = 0;
while (BFS()) max_flow += DFS(S,INF);
int ans = max_flow;
printf("%d\n",ans);
return 0;
}
24题建图
第一题:飞行员配对方案(二分图最大匹配)
建图:直接二分图建图,S连向所有皇家飞行员流量为INF,所有外国飞行员连向T流量为INF,每个皇家飞行员连向外国飞行员流量为1。最大流,最后输出所有的满流边的
void build() {
while (1)
{
scanf("%d%d",&x,&y);
if (x == y && y == -1) break;
if (x>y) swap(x,y);
add_Node(x,y,1);
}
}
第二题:太空飞行计划问题(最大权闭合子图)
建图:超级源连向所有正权点,流量为权值,负权点连向超级汇,流量为权值(取绝对值),对所有的正权和负权的限制,从正权点连向负权点一条边,流量为INF(这条边不会被剪掉)。跑最小割,答案 = 正权点权值和 - 最小割。
思路:剪掉一个正权点即为放弃一个收益,剪掉一个负权点即为放弃一个限制。
void build() {
scanf("%d%d",&m,&n);
S = ++cnt; T = ++cnt;
for (int i=1;i<=m;i++) l[i] = ++cnt;
for (int i=1;i<=n;i++) r[i] = ++cnt;
for (int i=1;i<=m;i++) {
scanf("%d",&v);
ans += v;
add_Node(S,l[i],v);
char ch = ' ';
while (ch != '\n') {
scanf("%d",&t); ch = getchar();
add_Node(l[i],r[t],INF);
}
}
for (int i=1;i<=n;i++) {
scanf("%d",&v); add_Node(r[i],T,v);
}
}
第三题:最小路径覆盖问题(有向无环图最小路径覆盖)
建图:将所有的点拆成两个点
思路:xi相当于一个点的出度点,yi相当于一个点的入度点。在最小路径覆盖问题中,一个点的出入度只能为0/1,所以它们连向超级源和超级汇的容量都为1。没有一条连向x,y的漫流边即为在路径覆盖中x,y被连接了。
void build() {
S = ++cnt; T = ++cnt;
for (int i=1;i<=n;i++) x[i] = ++cnt;
for (int i=1;i<=n;i++) add_Node(S,x[i],1);
for (int i=1;i<=n;i++) y[i] = ++cnt;
for (int i=1;i<=n;i++) add_Node(y[i],T,1);
for (int i=1;i<=m;i++) {
int u = x[ e[i].a ] , v = y[ e[i].y ];
add_Node(u,v,1);
}
}
建图:二分最大值,将所有的球拆点成xi,yi,分别连向超级源和超级汇,容量为1,对于球a,b(a容量为1,若点数-最大流<=柱子数目,则判定为true.
思路:最小路径覆盖问题
void build() {
S = ++cnt; T = ++cnt;
for (int i=1;i<=n;i++) x[i] = ++cnt;
for (int i=1;i<=n;i++) add_Node(S,x[i],1);
for (int i=1;i<=n;i++) y[i] = ++cnt;
for (int i=1;i<=n;i++) add_Node(y[i],T,1);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (floor( sqrt(i+j) ) == sqrt(i+j)) add_Node(i,j,1);
}
第五题:圆桌问题(二分图多重匹配)
建图:
S连向所有的单位,容量为该单位人数。
单位连向所有的餐桌,容量为1。
所有的餐桌连向T,容量为餐桌的容量。
最大流。输出所有满流的<单位、餐桌>边。
void build() {
scanf("%d%d",&n,&m);
S = ++cnt; T = ++cnt;
for (int i=1;i<=n;i++) {
scanf("%d",&t);
l[i] = ++cnt;
add_Node(S,l[i],t);
}
for (int i=1;i<=m;i++) {
scanf("%d",&t);
r[i] = ++cnt;
add_Node(r[i],T,t);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) add_Node(l[i],r[j],1);
}
诶一开始建的图是错的,少考虑了很多东西
第一问第二问DP,第三问网络流(需要借助前两问的dp数组)
令F[i]为最长的以i结尾的LIS的长度
建图:
对所有的点拆成xi,yi,且加入边xi,yi,如果是两端的点则容量为INF否则容量为1
对所有的F[i] = 1,S连向xi容量为1
对所有的F[i] = 第一问答案,yi连向T容量为1
对于每一对$ $满足$ F[i]+1 = F[j] $ , $i
最大流。
void build() {
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (F[i]+1 == F[j]) add_Node(i,j,1);
}
1-6题完成时间:2016年12月10日-2016年12月13日