ybtoj Au 网络流模型及应用
前言中的前言
-
由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻
-
由于本人过菜,不知道什么时候就
了,想给这个机房留下点什么…… -
如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人
正片开始!
好耶!!!(泪目)
题面
前言
本章虽然叫网络流模型及应用,但其实就是最小割
最小割 T1,板子题
正文
割贡献,第
这条就在
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+6;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m;
int s,t,head[N],cnt=1;
struct node{int to,nxt,w;}e[N];
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].nxt=head[u];
e[cnt].w=w;
head[u]=cnt;
}
int ans=0;
int deep[N],now[N];
bool bfs(){
for(int i=s;i<=t;i++)deep[i]=inf;
deep[s]=0;
now[s]=head[s];
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
deep[v]=deep[u]+1;
now[v]=head[v];
q.push(v);
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=now[u];~i&∑i=e[i].nxt){
now[u]=i;
int v=e[i].to;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(sum,e[i].w));
if(k==0)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
flow+=k;
sum-=k;
}
}
return flow;
}
signed main(){
cin>>n>>m;
t=n+1;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++){
int a,b;
cin>>a>>b;
add(s,i,a),add(i,s,0);
add(i,t,b),add(t,i,0);
}
for(int i=1;i<=m;i++){
int x,y,w;
cin>>x>>y>>w;
add(x,y,w),add(y,x,0),add(y,x,w),add(x,y,0);
}
while(bfs())ans+=dfs(s,inf);
cout<<ans;
return 0;
}
后记
好困好困
题面
前言
最小割 T2,应用题
正文
对于同时选的点,我们新建一个点
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+6;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m;
int S,T,s,t,head[N],cnt1=1;
struct node{int to,nxt,w;}e[N];
int id(int x, int y)
{
return (x - 1) * m + y;
}
void add(int u,int v,int w){
e[++cnt1].to=v;
e[cnt1].nxt=head[u];
e[cnt1].w=w;
head[u]=cnt1;
e[++cnt1].to=u;
e[cnt1].nxt=head[v];
e[cnt1].w=0;
head[v]=cnt1;
}
int ans=0;
int deep[N],now[N];
bool bfs(){
for(int i=s;i<=t;i++)deep[i]=inf;
deep[s]=0;
now[s]=head[s];
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
deep[v]=deep[u]+1;
now[v]=head[v];
q.push(v);
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=now[u];~i&∑i=e[i].nxt){
now[u]=i;
int v=e[i].to;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(sum,e[i].w));
if(k==0)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
flow+=k;
sum-=k;
}
}
return flow;
}
int tot;
signed main(){
cin>>n>>m;
memset(head,-1,sizeof(head));
int tmp = n * m;
s = 0;
t = n * m + (n - 1) * m * 2 + n * (m - 1) * 2 + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int x;
cin >> x;
add(s, (i - 1) * m + j, x);
tot += x;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int x;
cin >> x;
add((i - 1) * m + j, t, x);
tot += x;
}
}
for (int i = 1; i < n; i++) {
for (int j = 1; j <= m; j++) {
int x;
cin >> x;
tot += x;
add(s, ++tmp, x);
add(tmp, (i - 1) * m + j, inf);
add(tmp, i * m + j, inf);
}
}
for (int i = 1; i < n; i++) {
for (int j = 1; j <= m; j++) {
int x;
cin >> x;
tot += x;
add(++tmp, t, x);
add((i - 1) * m + j, tmp, inf);
add(i * m + j, tmp, inf);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < m; j++) {
int x;
cin >> x;
tot += x;
add(s, ++tmp, x);
add(tmp, (i - 1) * m + j, inf);
add(tmp, (i - 1) * m + j + 1, inf);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < m; j++) {
int x;
cin >> x;
tot += x;
add(++tmp, t, x);
add((i - 1) * m + j, tmp, inf);
add((i - 1) * m + j + 1, tmp, inf);
}
}
while(bfs())ans+=dfs(s,inf);
cout<<tot-ans;
return 0;
}
后记
网络流水紫真的好多啊
题面
前言
最小割 T3,水题
正文
奖金和代价是不能共存的,所以我们要割~
代码
#include <bits/stdc++.h>
using namespace std;
const int inf=1e9+9;
const int N=1e6+6;
int m,n,s,t,tot,head[N],cnt=1;
struct node{int to,nxt,w;}e[N];
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[cnt].w=w;
}
int ans,deep[N],now[N];
bool bfs(){
for(int i=0;i<=N-5;i++)deep[i]=inf;
//cout<<deep[s]<<endl;
deep[s]=0;
now[s]=head[s];
queue<int> q;
q.push(s);
//cout<<q.empty();
while(!q.empty()){
int u=q.front();q.pop();
//cout<<u<<endl;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
//cout<<v<<" ";
if(deep[v]==inf&&e[i].w){
q.push(v);
now[v]=head[v];
deep[v]=deep[u]+1;
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=now[u];i&∑i=e[i].nxt){
int v=e[i].to;
now[u]=i;
if((deep[v]==deep[u]+1)&&e[i].w){
k=dfs(v,min(sum,e[i].w));
if(!k)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
sum-=k;
flow+=k;
}
}
return flow;
}
int main(){
cin>>m>>n;
t=m+n+1;
for(int i=1;i<=m;i++){
int x;
cin>>x;
tot+=x;
add(s,i,x),add(i,s,0);
while(getchar()==' '){
cin>>x;
add(i,x+m,inf);
add(x+m,i,0);
}
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
add(i+m,t,x);
add(t,i+m,0);
}
while(bfs())ans+=dfs(s,inf);
for(int i=1;i<=m;i++)if(deep[i]!=inf)cout<<i<<" ";
cout<<endl;
for(int i=1;i<=n;i++)if(deep[i+m]!=inf)cout<<i<<" ";
cout<<endl<<tot-ans;
return 0;
}
后记
好摆啊
题面
前言
最小割 T4,典题
正文
割完之后分别从
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+6;
const int inf=1e9+9;
int n,m,s,t,tot,cnt,head[N];
bool vis[N];
struct node{int to,nxt,w;}e[N];
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[cnt].w=w;
}
int now[N],deep[N];
bool bfs(){
for(int i=0;i<N-5;i++)deep[i]=inf;
now[s]=head[s];
deep[s]=0;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
deep[v]=deep[u]+1;
now[v]=head[v];
q.push(v);
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=head[u];i&∑i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(e[i].w,sum));
if(!k)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
flow+=k;
sum-=k;
}
}
return flow;
}
void dfs1(int u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!vis[v]&&e[i].w){
tot++;
vis[v]=1;
dfs1(v);
}
}
}
void dfs2(int u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!vis[v]&&e[i^1].w){
tot++;
vis[v]=1;
dfs2(v);
}
}
}
int ans;
int main(){
cin>>n>>m>>s>>t;
while(n!=0&&m!=0){
tot=2,cnt=1,ans=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z),add(y,x,z);
}
while(bfs())ans+=dfs(s,inf);
vis[s]=vis[t]=1;
dfs1(s),dfs2(t);
if(tot==n)cout<<"UNIQUE"<<endl;
else cout<<"AMBIGUOUS"<<endl;
cin>>n>>m>>s>>t;
}
return 0;
}
后记
昨天梦见教练不让我去浙江……,醒了还没反应过来……
题面
前言
最小割 T5,云落秒了题
正文
其实最后呢,题意就可以转化为判
所以我们就从
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100005;
const int inf=1e18;
int n,m,u[N],v[N],head[N<<4],cnt=1;
double eps=1e-8;
struct node{int to,nxt;double w;}e[N<<4];
void add(int u,int v,double w){
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int s,t,now[N],deep[N];
bool bfs(){
for(int i=1;i<=t;i++)deep[i]=inf;
deep[s]=0;
now[s]=head[s];
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
q.push(v);
deep[v]=deep[u]+1;
now[v]=head[v];
if(v==t)return 1;
}
}
}
return 0;
}
double dfs(int u,double sum){
if(u==t)return sum;
double k,flow=0;
for(int i=head[u];i&∑i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(e[i].w,sum));
if(!k)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
flow+=k;
sum-=k;
}
}
return flow;
}
double op;
bool check(double x){
cnt=1;
s=m+n+1,t=s+1;
for(int i=1;i<=t;i++)head[i]=0;
for(int i=1;i<=m;i++){
add(s,i+n,1);
add(i+n,s,0);
add(i+n,u[i],inf);
add(u[i],i+n,0);
add(i+n,v[i],inf);
add(v[i],i+n,0);
}
for(int i=1;i<=n;i++){
add(i,t,x);
add(t,i,0);
}
op=0;
while(bfs()){
op+=dfs(s,inf);
}
// cout<<op<<endl;
return m-op>0;
}
int tot;
bool vis[N];
void dfs1(int x){
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
double w=e[i].w;
if(!vis[v]&&w)dfs1(v);
}
}
vector<int> o;
signed main(){
while(cin>>n>>m){
memset(vis,0,sizeof(vis));
if(m==0){cout<<1<<endl<<1<<endl;continue;}
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i];
}
double l=0,r=2e9,ans=-1;
while(l+eps<r){
double mid=(l+r)/2;
//cout<<mid<<endl;
if(check(mid))ans=mid,l=mid;
else r=mid;
}
check(ans);
dfs1(s);
tot=0;
o.clear();
for(int i=1;i<=n;i++){
if(vis[i]){
o.push_back(i);
tot++;
}
}
cout<<tot<<endl;
for(int i:o)cout<<i<<endl;
cout<<endl;
}
return 0;
}
后记
多测为啥过不了啊啊啊
题面
前言
最小割 T6,水题
正文
同 T2
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6;
const int inf=1e9;
int dx[6]={0,0,0,-1,1};
int dy[6]={0,-1,1,0,0};
int n,m,o,s,t,head[N],cnt=1;
struct node{int to,nxt,w;}e[N];
int id(int x,int y){return (x-1)*m+y;}
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].nxt=head[u];
e[cnt].w=w;
head[u]=cnt;
}
int res,ans;
int deep[N],now[N];
bool bfs(){
for(int i=1;i<=o;i++)deep[i]=inf;
now[s]=head[s];
deep[s]=0;
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
deep[v]=deep[u]+1;
now[v]=head[v];
q.push(v);
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=now[u];i&∑i=e[i].nxt){
int v=e[i].to;
now[u]=i;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(e[i].w,sum));
if(!k)deep[v]=inf;
e[i].w-=k;
e[i^1].w+=k;
sum-=k;
flow+=k;
}
}
return flow;
}
int main(){
cin>>n>>m;
t=id(n,m)+1,s=0;
o=t;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;
cin>>a;
res+=a;
add(s,id(i,j),a);
add(id(i,j),s,0);
// cout<<s<<" "<<id(i,j)<<endl;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;
cin>>a;
res+=a;
add(id(i,j),t,a);
add(t,id(i,j),0);
// cout<<id(i,j)<<" "<<t<<endl;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
o++;
int a;
cin>>a;
res+=a;
add(s,o,a);
add(o,s,0);
// cout<<s<<" "<<o<<endl;
for(int k=0;k<=4;k++){
int u=i+dx[k],v=j+dy[k];
if(u>=1&&u<=n&&v>=1&&v<=m){
add(o,id(u,v),inf);
add(id(u,v),o,0);
// cout<<o<<" "<<id(u,v)<<endl;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
o++;
int a;
cin>>a;
res+=a;
add(o,t,a);
add(t,o,0);
// cout<<o<<" "<<t<<endl;
for(int k=0;k<=4;k++){
int u=i+dx[k],v=j+dy[k];
if(u>=1&&u<=n&&v>=1&&v<=m){
add(id(u,v),o,inf);
add(o,id(u,v),0);
// cout<<o<<" "<<id(u,v)<<endl;
}
}
}
}
while(bfs())ans+=dfs(s,inf);
cout<<res-ans<<endl;
return 0;
}
后记
切力
题面
前言
最小割 T7,毒瘤题
正文
横竖不能交叉,考虑割
那么我们拆点,把一个点拆成横点和竖点,中间连
对于每个炮塔,到最大值的路径上两两连边,容量为最大值减边的起点的权值,所以,如果切了一条边就相当于打了这条边的起点,到最大值是因为打最大值往后的点更劣
代码
#include<bits/stdc++.h>
#define ll long long
#define rep(i,s,e) for(int i=(s);i<=(e);++i)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=6005;
const int dx[]={0,-1,1,0,0};
const int dy[]={0,0,0,-1,1};
int n,m,a[55][55],s,t,mp[55][55];
int head[N],rad[N],cnt=1;
struct qwq{
int u,v,w,nxt;
}e[N*N<<1];
struct qaq{
int x,y,op,tx,ty,mx;
};
vector<qaq> g;
inline void add(int u,int v,int w){
// cout<<u<<' '<<v<<' '<<w<<endl;
e[++cnt]={u,v,w,head[u]},head[u]=cnt;
e[++cnt]={v,u,0,head[v]},head[v]=cnt;
}
namespace dinic{
int dep[N];
inline bool bfs(){
queue<int> q;
rep(i,1,t) dep[i]=0;
dep[s]=1,q.push(s);
while(!q.empty()){
int x=q.front();q.pop();
rad[x]=head[x];
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(!dep[v]&&w) dep[v]=dep[x]+1,q.push(v);
}
}
return dep[t]!=0;
}
int dfs(int x,int flow){
if(x==t) return flow;
int out=0;
for(int &i=rad[x];i;i=e[i].nxt){
int v=e[i].v,&w=e[i].w;
if(dep[v]==dep[x]+1&&w){
int res=dfs(v,min(w,flow));
out+=res,flow-=res,w-=res,e[i^1].w+=res;
}
if(!flow) break;
}
if(flow) dep[x]=-1;
return out;
}
inline int solve(){
int ans=0,tmp;
while(bfs()) while(tmp=dfs(s,1e9)) ans+=tmp;
return ans;
}
}
inline bool pd(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;}
signed main(){
n=read(),m=read();
int id=0;
rep(i,1,n) rep(j,1,m) a[i][j]=read(),mp[i][j]=++id;
rep(i,1,n) rep(j,1,m) if(a[i][j]<0){
int op=-a[i][j],x=i,y=j,mx=0,tx=i,ty=j;
while(1){
x+=dx[op],y+=dy[op];
if(!pd(x,y)) break;
if(mx<a[x][y]) mx=a[x][y],tx=x,ty=y;
}
g.push_back({i,j,op,tx,ty,mx}),a[i][j]=0;
}
rep(i,1,id) add(i,id+i,1e9);
int tot=0;s=id<<1|1,t=s+1;
for(qaq now:g){
int i=now.x,j=now.y,op=now.op,mx=now.mx,pre;
tot+=mx;
if(op<=2){
add(mp[i][j]+id,t,1e9);
int x=now.tx,y=now.ty;
pre=mp[x][y]+id;
if(x==i&&y==j) continue;
while(1){
x-=dx[op],y-=dy[op];
add(pre,mp[x][y]+id,mx-a[x][y]),pre=mp[x][y]+id;
if(x==i&&y==j) break;
}
}
else{
add(s,mp[i][j],1e9),pre=mp[i][j];
int x=i,y=j,pw=mx;
if(x==now.tx&&y==now.ty) continue;
while(1){
x+=dx[op],y+=dy[op];
add(pre,mp[x][y],pw);
pw=mx-a[x][y],pre=mp[x][y];
if(x==now.tx&&y==now.ty) break;
}
}
}
printf("%d\n",tot-dinic::solve());
}
后记
好困好困
题面
前言
最小割 T8,综合题
正文
首先,式子是很好推的
对于每一个B矩阵中的元素
如果不要他(即
否则就必须损失
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int M=5e6+6;
const int N=1005;
const int inf=9e18;
int n,b[N][N],c[N],s,t,head[M],cnt=1,res,ans;
struct node{int to,nxt,w;}e[M];
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[cnt].w=w;
}
int id(int x,int y){return (x-1)*n+y;}
int now[M],deep[M];
bool bfs(){
now[s]=head[s];
for(int i=0;i<=t;i++)deep[i]=inf;
deep[s]=0;
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].w&&deep[v]==inf){
deep[v]=deep[u]+1;
now[v]=head[v];
q.push(v);
if(v==t)return 1;
}
}
}
return 0;
}
int dfs(int u,int sum){
if(u==t)return sum;
int k,flow=0;
for(int i=now[u];i&∑i=e[i].nxt){
now[u]=i;
int v=e[i].to;
if(e[i].w&&(deep[v]==deep[u]+1)){
k=dfs(v,min(sum,e[i].w));
if(!k)deep[v]=0;
e[i].w-=k;
e[i^1].w+=k;
flow+=k;
sum-=k;
}
}
return flow;
}
signed main(){
cin>>n;
s=id(n,n)+n+1,t=s+1;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>b[i][j];
for(int i=1;i<=n;i++){
cin>>c[i];
add(id(n,n)+i,t,c[i]);
add(t,id(n,n)+i,0);
//cout<<id(n,n)+i<<" "<<t<<endl;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
add(id(i,j),id(n,n)+i,inf);
add(id(n,n)+i,id(i,j),0);
add(id(i,j),id(n,n)+j,inf);
add(id(n,n)+j,id(i,j),0);
add(s,id(i,j),b[i][j]);
add(id(i,j),s,0);
res+=b[i][j];
// cout<<id(i,j)<<" "<<id(n,n)+i<<endl;
// cout<<id(i,j)<<" "<<id(n,n)+j<<endl;
// cout<<s<<" "<<id(i,j)<<endl;
}
}
while(bfs())ans+=dfs(s,inf);
cout<<res-ans;
return 0;
}
后记
再写真的要睡过去了……
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】