今天终于把kuangbin大佬的网络流专题给做完了,感觉这些题都是很棒的。在网络流题目中,比熟练掌握各种算法更重要的,是根据不同的题目建立一张图,有些题目看似与图论无关的题目,硬是可以抽象出一张图来,下面随意总结几种建图方法
1.拆点法
如果题目对通过某个点的流量有要求,可以把这个点拆成两个点,其中一个入点和一个出点,从入点到出点有一条容量为流量限制的边,然后以该点为终点的边都连到入点上,以该点为起点的边都连到出点上。
2.已知矩阵的每一行和每一列的和,求出一个满足条件的矩阵,其中矩阵的元素满足[1,max]
每一行当作一个点,每一列当作一个点。由于网络流允许出现零流,但是数据范围不允许,从超级源点s到每一行加一条容量为该行的和减去列数的边,从每一列的点建一条到汇点的流量为该列列的和减去行数的边,从每一行到每一列建一条容量为max-1的边,流量表示该行该列交叉处元素数的值-1,最后输出时加一就行了。其他数据范围同理
3.状态压缩
该题中点的数量(100000)远大于状态的数量(1024),可以把相同状态的点合并,节省时间,不然会tle
细节见代码
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxv = 1500, maxe = 30000;
struct Edge
{
int to, residual, nxt;
Edge() {}
Edge(int v,int cap,int nxt):to(v),residual(cap),nxt(nxt){}
} edge[maxe];
int n, m, ecnt, vnum,s,t;
int head[maxv];
void addEdge(int u, int v, int c)
{
edge[ecnt++] = Edge(v, c, head[u]);
edge[ecnt++] = Edge(u, 0, head[v]);
head[u] = ecnt - 2;
head[v] = ecnt - 1;
}
int cure[maxv], d[maxv];
bool vis[maxv];
bool bfs()
{
queue<int> q;
q.push(s);
memset(vis, 0, sizeof(bool)*vnum);
d[s] = 0;
vis[s] = true;
while (!q.empty())
{
int x = q.front(); q.pop();
for (int cur = head[x]; cur != -1; cur = edge[cur].nxt)
{
Edge& e = edge[cur];
if (e.residual && !vis[e.to])
{
vis[e.to] = true;
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& cur = cure[x]; cur != -1; cur = edge[cur].nxt)
{
Edge& e = edge[cur];
if (d[e.to] == d[x] + 1 && (f= dfs(e.to, min(a, e.residual))) > 0)
{
a -= f;
flow += f;
e.residual -= f;
edge[cur ^ 1].residual += f;
if (a == 0) break;
}
}
return flow;
}
int dinic()
{
int flow = 0;
while (bfs())
{
memcpy(cure, head, sizeof(int)*vnum);
flow += dfs(s, 0x3f3f3f3f);
}
return flow;
}
int num[1024];//保存该状态的点的数量
int main()
{
while (~scanf("%d%d", &n, &m))
{
vnum = (1<<m) + m + 2;
s = vnum - 2;
t = vnum - 1;
ecnt = 0;
memset(head, -1, sizeof(int)*vnum);
memset(num, 0, sizeof(int)*(1 << m));
for (int i = 0,w; i < n; i++)
{
int x = 0;
for (int j = 0; j < m; j++)
{
scanf("%d", &w);
if (w) x |= (1 << j);
}
num[x]++;
}
for (int i = 0; i < (1 << m); i++)
if (num[i])
{
addEdge(s, i, num[i]);
for (int j = 0; j < m; j++)
if (i&(1 << j))
addEdge(i, (1 << m) + j, num[i]);
}
for (int i = 0,c; i < m; i++)
{
scanf("%d", &c);
addEdge((1<<m)+ i,t, c);
}
printf("%s\n", dinic() == n ? "YES" : "NO");
}
return 0;
}