1月15日
1月15日
今天没有思维题,做了最大流题单。
P3376 【模板】网络最大流 - 洛谷 | 计算机科学教育新生态
模板题
#include<bits/stdc++.h>
#define int long long
#define N 10010
#define M 200010
using namespace std;
int n,m,S,T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];
void add(int a,int b,int c){
e[++idx]={b,c,h[a]};
h[a]=idx;
}
bool bfs(){ //对点分层,找增广路
memset(d,0,sizeof d);
queue<int>q;
q.push(S); d[S]=1;
while(q.size()){
int u=q.front(); q.pop();
for(int i=h[u];i;i=e[i].last){
int v=e[i].v;
if(d[v]==0 && e[i].c){
d[v]=d[u]+1;
q.push(v);
if(v==T)return true;
}
}
}
return false;
}
int dfs(int u, int mf){ //多路增广
if(u==T) return mf;
int sum=0;
for(int i=cur[u];i;i=e[i].last){
cur[u]=i; //当前弧优化
int v=e[i].v;
if(d[v]==d[u]+1 && e[i].c){
int f=dfs(v,min(mf,e[i].c));
e[i].c-=f;
e[i^1].c+=f; //更新残留网
sum+=f; //累加u的流出流量
mf-=f; //减少u的剩余流量
if(mf==0)break;//余量优化
}
}
if(sum==0) d[u]=0; //残枝优化
return sum;
}
int dinic(){ //累加可行流
int flow=0;
while(bfs()){
memcpy(cur, h, sizeof h);
flow+=dfs(S,1e9);
}
return flow;
}
signed main() {
int a, b, c;
cin>>n>>m>>S>>T;
while (m--) {
cin>>a>>b>>c;
add(a, b, c);
add(b, a, 0);
}
cout<<dinic()<<endl;
return 0;
}
P4722 【模板】最大流 加强版 / 预流推进 - 洛谷 | 计算机科学教育新生态
因为dinic的复杂度上限是O(n^{2}*m),所以会有卡dinic的题,所以就有了预留推进算法——hlpp,实在是太抽象,没看懂也懒得看,所以就记了个板子。以后再看看。
#include <bits/stdc++.h>
using namespace std;
inline int Read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
inline void write(int x){
if(x < 0){//是负数提前输出负号
putchar('-');//字符输出
x = -x;//转成正数处理
}
if(x > 9){//不是一位数
write(x / 10);//递归——直到最高位
}
putchar(x % 10 + '0');//转成字符输出
return;
}
const int inf=1<<30;
int top=1,head[10100];
int n,m,s,t;
int e[10100],h[10100],cnth[20100];//每个点对应的余流,高度;每个高度有多少个点
struct cmp{
inline bool operator () (int a,int b) const{
return h[a]<h[b];
}
};
struct Node{
int v;
int val;
int next;
}node[400100];
inline void addedge(int u,int v,int val){
node[++top].v=v;
node[top].val=val;
node[top].next=head[u];
head[u]=top;
return;
}
inline void add(int u,int v,int val){
addedge(u,v,val);
addedge(v,u,0);
return;
}
int inque[11000];
void bfs(){
memset(h,0x3f,sizeof(h));
h[t]=0;
queue<int>qu;
qu.push(t);
while(!qu.empty()){
int u=qu.front();
qu.pop();
inque[u]=0;
for(int i=head[u];i;i=node[i].next){
int d=node[i].v;
if(node[i^1].val&&h[d]>h[u]+1){//反向跑
h[d]=h[u]+1;
if(inque[d]==0){
qu.push(d);
inque[d]=1;
}
}
}
}
return;
}
priority_queue<int,vector<int>,cmp>q;
inline void push_(int u){
for(int i=head[u];i;i=node[i].next){
int d=node[i].v;
if(node[i].val&&h[d]+1==h[u]){//可以推流
int mi=min(node[i].val,e[u]);
node[i].val-=mi;
node[i^1].val+=mi;
e[u]-=mi;
e[d]+=mi;
if(inque[d]==0&&d!=t&&d!=s){
q.push(d);
inque[d]=1;
}
if(e[u]==0)break;//已经推完了
}
}
return;
}//推流
inline void relabel(int u){
h[u]=inf;
for(int i=head[u];i;i=node[i].next){
int d=node[i].v;
if(node[i].val&&h[d]+1<h[u]){
h[u]=h[d]+1;
}
}
return;
}//把u的高度更改为与u相邻的最低的点的高度加1
int hlpp(){
int i;
bfs();
if(h[s]==0x3f3f3f3f)return 0;//s与t不连通
h[s]=n;
for(i=1;i<=n;i++)if(h[i]<0x3f3f3f3f)cnth[h[i]]++;//统计各个高度的点数,注意不要让下标越界
for(i=head[s];i;i=node[i].next){
int d=node[i].v;
int mi=node[i].val;
if(mi){
e[s]-=mi;
e[d]+=mi;
node[i].val-=mi;
node[i^1].val+=mi;
if(d!=t&&inque[d]==0&&d!=s){
q.push(d);
inque[d]=1;
}
}
}//从s向周围点推流
while(!q.empty()){
int u=q.top();
inque[u]=0;
q.pop();
push_(u);
if(e[u]){//还有余流
cnth[h[u]]--;
if(cnth[h[u]]==0){
for(int i=1;i<=n;i++){
if(i!=s&&i!=t&&h[i]>h[u]&&h[i]<n+1){
h[i]=n+1;//标记无法到达
}
}
}//gap优化
relabel(u);
cnth[h[u]]++;
q.push(u);
inque[u]=1;
}
}
return e[t];
}
signed main(){
n=Read(),m=Read(),s=Read(),t=Read();
int i;
int u,v,val;
for(i=1;i<=m;i++)u=Read(),v=Read(),val=Read(),add(u,v,val);
printf("%d\n",hlpp());
return 0;
}
[P2740 USACO4.2] 草地排水Drainage Ditches - 洛谷 | 计算机科学教育新生态
就是最大流的板子题
#include<bits/stdc++.h>
#define int long long
#define N 10010
#define M 200010
using namespace std;
int n,m;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];
void add(int a,int b,int c){
e[++idx]={b,c,h[a]};
h[a]=idx;
}
bool bfs(int S){ //对点分层,找增广路
memset(d,0,sizeof d);
queue<int>q;
q.push(S); d[S]=1;
while(q.size()){
int u=q.front(); q.pop();
for(int i=h[u];i;i=e[i].last){
int v=e[i].v;
if(d[v]==0 && e[i].c){
d[v]=d[u]+1;
q.push(v);
if(v==T)return true;
}
}
}
return false;
}
int dfs(int u, int mf){ //多路增广
if(u==T) return mf;
int sum=0;
for(int i=cur[u];i;i=e[i].last){
cur[u]=i; //当前弧优化
int v=e[i].v;
if(d[v]==d[u]+1 && e[i].c){
int f=dfs(v,min(mf,e[i].c));
e[i].c-=f;
e[i^1].c+=f; //更新残留网
sum+=f; //累加u的流出流量
mf-=f; //减少u的剩余流量
if(mf==0)break;//余量优化
}
}
if(sum==0) d[u]=0; //残枝优化
return sum;
}
int dinic(int S){ //累加可行流
int flow=0;
while(bfs(S)){
memcpy(cur, h, sizeof h);
flow+=dfs(S,1e9);
}
return flow;
}
signed main() {
cin>>n>>m;
for(int i=1;i<=n;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,0);
}
T=m;
cout<<dinic(1)<<endl;
return 0;
}
[P2065 TJOI2011] 卡片 - 洛谷 | 计算机科学教育新生态
最大流的匹配问题
题意:桌子上有红蓝两种颜色的卡牌,并且每张卡牌有一个数,目的是拿走一些组卡牌,每一组所有卡牌颜色不能一致,且任意两张卡牌不互质。问:最多可以从桌上拿走多少组卡片。
思路:首先可以明白,每组卡牌选两张是最优的,建造网络模型,将红蓝颜色分为两组,并且将不互质的数都与质因子建边,且流量都为1(因为每张牌只选一次)。所以我们先质数筛,然后建边,跑最大流就行了。最大流即为答案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=10010,M=200010;
int n,m;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];
void add(int a,int b,int c) {
e[++idx] = {b, c, h[a]};
h[a] = idx;
}
bool bfs(int S) { //对点分层,找增广路
memset(d, 0, sizeof d);
queue<int> q;
q.push(S);
d[S] = 1;
while (q.size()) {
int u = q.front();
q.pop();
for (int i = h[u]; i; i = e[i].last) {
int v = e[i].v;
if (d[v] == 0 && e[i].c) {
d[v] = d[u] + 1;
q.push(v);
if (v == T)return true;
}
}
}
return false;
}
int dfs(int u, int mf) { //多路增广
if (u == T) return mf;
int sum = 0;
for (int i = cur[u]; i; i = e[i].last) {
cur[u] = i; //当前弧优化
int v = e[i].v;
if (d[v] == d[u] + 1 && e[i].c) {
int f = dfs(v, min(mf, e[i].c));
e[i].c -= f;
e[i ^ 1].c += f; //更新残留网
sum += f; //累加u的流出流量
mf -= f; //减少u的剩余流量
if (mf == 0)break;//余量优化
}
}
if (sum == 0) d[u] = 0; //残枝优化
return sum;
}
int dinic(int S) { //累加可行流
int flow = 0;
while (bfs(S)) {
memcpy(cur, h, sizeof h);
flow += dfs(S, 1e9);
}
return flow;
}
int blue[505],red[505];
int vis[N]; //划掉合数
int prim[N]; //记录质数
int cnt; //质数个数
void get_prim(int n) { //欧拉筛法-----O(N)
vis[1]=1;
for (int i = 2; i <= n; i++) {//越界中断
if (!vis[i]) prim[++cnt] = i;
for (int j = 1; i * prim[j] <= n; j++) {//乘以已经记录的数,越界中断,开筛
vis[i * prim[j]] = 1;
if (i % prim[j] == 0) break;//整除中断,保证被最小的质因子prim[j]划掉
}
}
}
void init() {
memset(h, 0, sizeof h);
idx = 1;
}
void solve() {
init();
cin >> m >> n;
T = 1e7;
int num = 1;
for (int i = 1; i <= m; i++) cin >> blue[i];
for (int i = 1; i <= n; i++) cin >> red[i];
for (int i = 1; i <= m; i++) {
add(1, i + 1, 1);
add(i + 1, 1, 0);
}
num += m;
for (int i = 1; i <= m; i++) {
int tmp = blue[i];
int pos = i + 1;
for (int j = 1; j <= cnt; j++) {
if (prim[j] > tmp) break;
if (tmp % prim[j] == 0) {
add(pos, num + j, 1);
add(num + j, pos, 0);
}
}
}
int num1 = num + cnt;
map<int, int> mp;
for (int i = 1; i <= n; i++) {
mp[i] = ++num1;
}
for (int i = 1; i <= n; i++) {
int tmp = red[i];
for (int j = 1; j <= cnt; j++) {
if (prim[j] > tmp) break;
if (tmp % prim[j] == 0) {
add(num + j, mp[i], 1);
add(mp[i], num + j, 0);
}
}
}
T = num1 + 1;
for (int i = 1; i <= n; i++) {
add(mp[i], T, 1);
add(T, mp[i], 0);
}
cout << dinic(1) << endl;
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _;
cin >> _;
int nn = 1e4;
get_prim(nn);
while (_--)
solve();
return 0;
}
和上题思路差不多,但是问题目的不同,求的是任意一个方案,上题是最大的组数,跑完最大流之后,剩余流量为0的点就是被选的数。建图和上题类似。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=10010,M=200010;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];
void add(int a,int b,int c) {
e[++idx] = {b, c, h[a]};
h[a] = idx;
}
bool bfs(int S) { //对点分层,找增广路
memset(d, 0, sizeof d);
queue<int> q;
q.push(S);
d[S] = 1;
while (q.size()) {
int u = q.front();
q.pop();
for (int i = h[u]; i; i = e[i].last) {
int v = e[i].v;
if (d[v] == 0 && e[i].c) {
d[v] = d[u] + 1;
q.push(v);
if (v == T)return true;
}
}
}
return false;
}
int dfs(int u, int mf) { //多路增广
if (u == T) return mf;
int sum = 0;
for (int i = cur[u]; i; i = e[i].last) {
cur[u] = i; //当前弧优化
int v = e[i].v;
if (d[v] == d[u] + 1 && e[i].c) {
int f = dfs(v, min(mf, e[i].c));
e[i].c -= f;
e[i ^ 1].c += f; //更新残留网
sum += f; //累加u的流出流量
mf -= f; //减少u的剩余流量
if (mf == 0)break;//余量优化
}
}
if (sum == 0) d[u] = 0; //残枝优化
return sum;
}
int dinic(int S) { //累加可行流
int flow = 0;
while (bfs(S)) {
memcpy(cur, h, sizeof h);
flow += dfs(S, 1e9);
}
return flow;
}
void init() {
memset(h, 0, sizeof h);
idx = 1;
}
vector<int> ans[N];
void solve() {
init();
int k,n;
cin>>k>>n;
for(int i=1;i<=n;i++){
add(0,i,1);
add(i,0,0);
}
map<int,int> mp;
int cnt=n;
for(int i=1;i<=k;i++){
mp[i]=++cnt;
}
T=cnt+1;
vector<int> num(k+1,0);
for(int i=1;i<=k;i++){
int x;
cin>>x;
num[i]=x;
}
for(int i=1;i<=n;i++){
int p;
cin>>p;
for(int j=1;j<=p;j++){
int y;
cin>>y;
add(i,mp[y],1);
add(mp[y],i,0);
}
}
for(int i=1;i<=k;i++){
add(mp[i],T,num[i]);
add(T,mp[i],0);
}
dinic(0);
for(int i=1;i<=n;i++){
for(int j=h[i];j;j=e[j].last){
if(e[j].c==0){
ans[e[j].v].push_back(i);
}
}
}
for(int i=1;i<=k;i++){
if(ans[mp[i]].size()<num[i]){
cout<<"No Solution!"<<endl;
return;
}
}
for(int i=1;i<=k;i++){
cout<<i<<": ";
int cc=0;
for(auto j:ans[mp[i]]){
if(cc>=num[i]) break;
cc++;
cout<<j<<" ";
}
cout<<endl;
}
}
signed main() {
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
// int _;
// cin >> _;
// while (_--)
solve();
return 0;
}
最大流匹配问题总结
总结一下最大流的匹配问题
网络流擅长于解决各种有条件要求的匹配,重在如何建图。
建图要首先构造出网络模型:
- 将所有待匹配的点分为几组,建立两个虚拟点,也就是源点和汇点,没有组别就谈不上匹配了。
- 将点建边,边流量的确定是依据选点的限制条件来的,比如每组中的点只选一个,则组和组中点的边流量为1,
- 依据题目目的求网络流相应的结果。

浙公网安备 33010602011771号