Dinic算法相较于EK()算法效率更高
P3254 圆桌问题
思路:可用二分图来写,本题在于练习Dinic算法。
1.建立一个超级源点,连接每个代表团,权值为每个代表团的人数;
2.建立一个超级汇点,将每个圆桌连接汇点,权值为圆桌可容纳的人数;
3.将每个代表团连接每个圆桌,由于每个代表团不会安排两个人去同一个圆桌,因此权值都为1.
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
struct edge
{
int to,c,nxt;
}e[N];
int n,m,r[N],c[N],s,t,sum;
int head[N],idx=1;
int d[N],cur[N];
void add(int a,int b,int c)
{
e[++idx]={b,c,head[a]};
head[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=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(d[v]==0&&e[i].c)
{
d[v]=d[u]+1;
q.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
int dfs(int u,int mf)
{
if(u==t) return mf;
int sum=0;
for(int i=cur[u];i;i=e[i].nxt)
{
cur[u]=i;
int v=e[i].to;
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;
mf-=f;
if(mf==0) break;
}
}
if(sum==0) d[u]=0;
return sum;
}
int dinic()
{
int flow=0;
while(bfs())
{
memcpy(cur,head,sizeof head);
flow+=dfs(s,inf);
}
return flow;
}
void solve()
{
cin>>m>>n;
s=0,t=n+m+1;
for(int i=1;i<=m;i++)
{
cin>>r[i];sum+=r[i];
add(0,i,r[i]); //建立一个超级源点
add(i,0,0);
}
for(int i=1;i<=n;i++)
{
cin>>c[i];
add(i+m,t,c[i]);//建立一个超级汇点
add(t,i+m,0);
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
add(i,j+m,1);
add(j+m,i,0);
}
}
int ans=dinic();
if(ans!=sum)
cout<<0<<endl;
else
{
cout<<1<<endl;
for(int k=1;k<=m;k++)
{
for(int i=head[k];i;i=e[i].nxt)
{
if(e[i].to>m&&e[i].to<=m+n&&!e[i].c)
cout<<e[i].to-m<<" ";
}
cout<<endl;
}
}
}
signed main()
{
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
}
最小割
大佬讲解:https://www.bilibili.com/video/BV1iG411s7iX?spm_id_from=333.999.0.0&vd_source=91973ada1213cf6ba2cbb43a2bebd2e8
最小割最大流定理:最小割的容量等于最大流的容量
P1344 [USACO4.4]追查坏牛奶Pollutant Control
三个问题:
最小割、最小割的划分、最小割的最少边数
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
struct edge
{
int to,c,nxt;
}e[N];
int n,m,a[N],b[N],s,t;
int head[N],idx=1;
int d[N],cur[N];
void add(int a,int b,int c)
{
e[++idx]={b,c,head[a]};
head[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=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(d[v]==0&&e[i].c)
{
d[v]=d[u]+1;
q.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
int dfs(int u,int mf)
{
if(u==t) return mf;
int sum=0;
for(int i=cur[u];i;i=e[i].nxt)
{
cur[u]=i;
int v=e[i].to;
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;
mf-=f;
if(mf==0) break;
}
}
if(sum==0) d[u]=0;
return sum;
}
int dinic()
{
int flow=0;
while(bfs())
{
memcpy(cur,head,sizeof head);
flow+=dfs(s,inf);
}
return flow;
}
bool vis[N];
void mincut(int u)
{
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(!vis[v]&&e[i].c)
mincut(v);
}
}
void solve()
{
cin>>m>>n;
s=1,t=n;
for(int i=1,c;i<=m;i++)
{
cin>>a[i]>>b[i]>>c;
add(a[i],b[i],c);
add(b[i],a[i],0);
}
//最小割的容量
cout<<dinic()<<endl;
//最小割的划分
for(int i=1;i<=n;i++) // S
if(vis[i])
cout<<i<<" ";
for(int i=1;i<=n;i++) // T
if(!vis[i])
cout<<i<<" ";
//最小割最少边数
idx=1;
memset(head,0,sizeof head);
for(int i=1;i<=m;i++)
{
add(a[i],b[i],1);
add(b[i],a[i],0);
}
cout<<dinic()<<endl;
}
signed main()
{
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
}
C. Directional Increase
思路:
1.可通过加一移动到下一个元素,也可通过减1操纵移动到前一个元素,由于是从1开始,最后回到位置1,因此前缀和必定是>=0的。
2.在最后后一个非0元素由于要回到1,因此肯定为负数,前缀和肯定为0.
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=1e6+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,a[N],sum[N];
void solve()
{
cin>>n;
for(int i=0;i<=n;i++)
sum[i]=0;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
int flag=1,idx=1;
for(int i=n;i>=1;i--)
{
if(a[i])
{
idx=i;break;
}
}
if(sum[n]) flag=0;
for(int i=1;i<=n;i++)
{
if(sum[i]<=0&&i!=idx)
{
flag=0;break;
}
else if(i==idx)
break;
}
if(flag)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
signed main()
{
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
}
D. Fake Plastic Trees
思路:
1.贪心思想,需要对每个叶子结点都操作一次,本条路径上所有经过的点都加上该值。
2.利用深搜,若大于r[u],则返回r[u];若大于l[u],则返回累加值s
3.若都不满足,需要再进行一次操作,返回r[u]
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=1e6+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,cnt,head[N],l[N],r[N],ans;
struct node
{
int to,nxt;
}e[N];
void add(int from,int to)
{
e[++cnt].to=to;
e[cnt].nxt=head[from];
head[from]=cnt;
}
int dfs(int u,int fa)
{
int s=0;
for(int i=head[u];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==fa) continue;
s+=dfs(j,u);
}
if(head[u]==-1)
{
ans++;return r[u];
}
if(s>=r[u]) return r[u];
if(s>=l[u]) return s;
ans++;
return r[u];
}
void solve()
{
cin>>n;
cnt=ans=0;
for(int i=0;i<=n;i++)
head[i]=0;
for(int i=2;i<=n;i++)
{
int x;cin>>x;
add(x,i);
}
for(int i=1;i<=n;i++)
cin>>l[i]>>r[i];
dfs(1,0);
cout<<ans<<endl;
}
signed main()
{
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
}