- P1344 [USACO4.4]追查坏牛奶Pollutant Control
- P1345 [USACO5.4]奶牛的电信Telecowmunication
- P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查
- P2598 [ZJOI2009]狼和羊的故事
- P2774 方格取数问题
- P4126 [AHOI2009]最小割
- P5039 [SHOI2010]最小生成树
1.P1344 [USACO4.4]追查坏牛奶Pollutant Control
第一问求最小割
第二问求最小割边数
一个巧妙的方法就是: *一个比 m m m大的数,答案除,边数模
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf =0x3f3f3f3f,maxn=1e5+5,maxm=4e5+5;
int cur[maxn],head[maxn],dep[maxn],mm;
int cnt=1,maxflow=0,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=0;i<=n+5;i++){//如果要拆点或者其他的一定记得把范围开大点!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
signed main(){
scanf("%lld%lld",&n,&m);
s=1,t=n;
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&w);
add_edge(u,v,w*2020+1);
}
int ans=dinic();
printf("%lld %lld",ans/2020,ans%2020);
return 0;
}
2.P1345 [USACO5.4]奶牛的电信Telecowmunication
拆点最小割即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
const int inf=0x3f3f3f3f;
int dep[maxn],inque[maxn];
int top=1;
int maxflow,n,m,s,t;
struct edge{
int v,w,rev;
};
vector<edge>e[2*maxm];
inline void add_edge(int u,int v,int w){
e[u].push_back((edge){v,w,e[v].size()});
e[v].push_back((edge){u,0,e[u].size()-1});
}
bool bfs(){
memset(dep,inf,sizeof(dep));
memset(inque,0,sizeof(inque));
dep[s]=0;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=0;i<e[u].size();i++){
int v=e[u][i].v;
edge x=e[u][i];
if(dep[v]>dep[u]+1&&x.w){
dep[v]=dep[u]+1;
if(inque[v]==0){
q.push(v);
inque[v]=1;
}
}
}
}
if(dep[t]!=inf)return true;
return false;
}
int dfs(int u,int flow){
int rlow=0;
if(u==t)return flow;
for(int i=0;i<e[u].size();i++){
int v=e[u][i].v;
if(e[u][i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(flow,e[u][i].w))){
e[u][i].w-=rlow;
e[v][e[u][i].rev].w+=rlow;
return rlow;
}
}
}
return 0;
}
int dinic(){
int lowflow;
while(bfs()){
while(lowflow=dfs(s,inf))maxflow+=lowflow;
}
return maxflow;
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&t);
int u,v,w;
s=s+n;
for(int i=1;i<=n;i++){
add_edge(i,i+n,1);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
add_edge(u+n,v,inf);
add_edge(v+n,u,inf);
}
printf("%d",dinic());
return 0;
}
3.P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查
每一对好朋友之间连双向边,代表想要对方和自己一样的立场,求出最小割即为最小冲突数
#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=10000,maxm=2000005;
int ans1,mm,tot;
int cur[maxn],head[maxn],dep[maxn];
int cnt=1,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=0;i<=tot+5;i++){
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
int main(){
int x;
scanf("%d%d",&n,&m);
int u,v,w;
s=0,t=n+1;
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&x);
add_edge(0,i,x);
add_edge(i,t,x==1?0:1);
ans+=1;
}
tot=t;
int x1,x2;
for(int i=1;i<=m;i++){
scanf("%d%d",&x1,&x2);
add_edge(x1,x2,1);
add_edge(x2,x1,1);
}
printf("%d",dinic());
return 0;
}
4.P2598 [ZJOI2009]狼和羊的故事
所有的点跟自己周围的点连边
源点跟1连边
2跟汇点连边
求最小割
#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=1e5+5,maxm=4e5+5;
int cur[maxn],head[maxn],dep[maxn];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,-1,1};
int cnt=1,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=0;i<=t+5;i++){//如果要拆点或者其他的一定记得把范围开大点!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
inline int id(int x,int y){
return (x-1)*m+y;
}
int f[maxn],num[maxn];
int main(){
scanf("%d%d",&n,&m);
int u,v,w;
s=0,t=n*m+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&f[id(i,j)]);
if(f[id(i,j)]==1){
add_edge(s,id(i,j),inf);
}
if(f[id(i,j)]==2){
add_edge(id(i,j),t,inf);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<4;k++){
int xx=i+dx[k];
int yy=j+dy[k];
if(xx>=1&&yy<=m&&xx<=n&&yy>=1)add_edge(id(i,j),id(xx,yy),1);
}
}
}
printf("%d",dinic());
return 0;
}
5.P2774 方格取数问题
相邻的点为不同颜色
汇点向白点连边, w = w= w=点权
黑点向汇点连边, w = w= w=点权
白点向相邻的黑点连边, w = i n f w=inf w=inf
求最小割
其实也可以看做最大权闭合子图
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf =0x3f3f3f3f,maxn=1e5+5,maxm=4e5+5;
int cur[maxn],head[maxn],dep[maxn];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,-1,1};
int cnt=1,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=0;i<=t+5;i++){//如果要拆点或者其他的一定记得把范围开大点!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
inline int id(int x,int y){
return (x-1)*m+y;
}
int f[maxn],se[maxn],sum;
signed main(){
scanf("%lld%lld",&n,&m);
int u,v,w;
s=0,t=n*m+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&f[id(i,j)]);
sum+=f[id(i,j)];
if((i+j)%2==1){
se[id(i,j)]=1;
add_edge(s,id(i,j),f[id(i,j)]);
}
else if((i+j)%2==0){
add_edge(id(i,j),t,f[id(i,j)]);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)%2==1){
int dd=id(i,j);
int ff=f[dd];
// printf("%d ",ff);
for(int k=0;k<4;k++){
int xx=i+dx[k];
int yy=j+dy[k];
if(xx>=1&&yy<=m&&xx<=n&&yy>=1){
add_edge(id(i,j),id(xx,yy),inf);
}
}
}
}
}
printf("%lld",sum-dinic());
return 0;
}
6.P4126 [AHOI2009]最小割
问题1:求最小割的可行边
解法:两点不属于同一连通分量,且此边满流
if(e[i].w==0&&col[u]!=col[v]){
xx=1;
}
else xx=0;
问题2:求最小割的必要边
解法:在1的条件下,两点分别和源点和汇点属于同一强连通分量
if(e[i].w==0&&col[u]==col[s]&&col[v]==col[t]&&col[u]!=col[v]){
yy=1;
}
else yy=0;
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=4005,maxm=2e5+5;
int cur[maxn],head[maxn],dep[maxn];
int cnt=1,n,m,s,t,qq,cnt2=1;
struct edge{
int u,v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].u=u;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].u=u;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof head);
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
namespace Tarjan {
std::vector<int>v[maxn];
int dfn[maxn],low[maxn],col[maxn],st[maxn],f[maxn];
int top,p,cnt,mid,kk;
std::pair<int,int>ans[60005];
void tarjan(int x) {
dfn[x]=low[x]=++cnt;
f[x]=1,st[++top]=x;
for(int i=0;i<v[x].size();++i) {
int j=v[x][i];
if(!dfn[j]) tarjan(j),low[x]=min(low[x],low[j]);
else if(f[j]) low[x]=min(low[x],dfn[j]);
}
if(dfn[x]==low[x]) {
++p;
do {
mid=st[top--];
f[mid]=0;
col[mid]=p;
}while(mid!=x);
}
}
void solve() {
int qq=0;
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=e[j].nex)
if(e[j].w) v[i].push_back(e[j].v);
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=2;i<kk;i+=2) {
int xx,yy;
int u=e[i].u;
int v=e[i].v;
if(e[i].w==0&&col[u]!=col[v]){
xx=1;
}
else xx=0;
if(e[i].w==0&&col[u]==col[s]&&col[v]==col[t]&&col[u]!=col[v]){
yy=1;
}
else yy=0;
ans[++qq]=make_pair(xx,yy);
}
for(int i=1;i<=qq;i++){
printf("%d %d\n",ans[i].first,ans[i].second);
}
}
}
signed main(){
scanf("%d%d%d%d",&n,&m,&s,&t);
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
// cout<<cnt<<endl;
}
int yaoyao=dinic();
Tarjan::kk=cnt;
Tarjan::solve();
return 0;
}
7.P5039 [SHOI2010]最小生成树
题意转化:选择一条边问增加多少次边权能让指定边成为最小生成树的必须边
选择所有边权小于指定边的边,重新赋权为 c [ l a b ] − c [ i ] + 1 c[lab]-c[i]+1 c[lab]−c[i]+1
求出最小割,即最少操作多少次使图不连通,指定边就成为了最小生成树的必要边
#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=2005,maxm=20005;
int cur[maxn],head[maxn],dep[maxn];
int cnt=1,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=1;i<=n;i++){//如果要拆点或者其他的一定记得把范围开大点!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
int a[maxn],b[maxn],c[maxn];
int main(){
int lab;
scanf("%d%d%d",&n,&m,&lab);
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a[i],&b[i],&c[i]);
}
s=a[lab];
t=b[lab];
for(int i=1;i<=m;i++){
if(i==lab)continue;
if(c[i]<=c[lab]){
add_edge(a[i],b[i],c[lab]-c[i]+1);
add_edge(b[i],a[i],c[lab]-c[i]+1);
}
}
printf("%d",dinic());
return 0;
}