网络流杂题

美食节
动态加边的思想很棒qwq,我们SPFA的时候有的点显然用不到的(费用大不会增广)所以先不加这些边。
然后就是。。洛谷这题。。卡常数,有2个点不开O2我卡不进去啊qwqwq

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e7+5;
const int INF=1e9+7;

struct edge{
    int u,to,next,w,c;
}e[MAXN<<1];

int n,m,s,t,tot=0;

int head[MAXN],cnt=1;
inline void add(int u,int v,int w,int cost){
    e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;
    e[++cnt]=(edge){v,u,head[v],0,-cost},head[v]=cnt;
}

queue<int>qq;
int dis[81005],pre[81005];
bool vis[81005];

bool SPFA(int x){
    memset(vis,0,sizeof(vis));
    memset(pre,0,sizeof(pre));
    for(int i=s;i<=t;i++)dis[i]=INF;
    qq.push(x);
    dis[x]=0;vis[x]=1;
    while(qq.size()){
        int u=qq.front();qq.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].c;
            if(e[i].w){
                if(dis[u]+w<dis[v]){
                    dis[v]=dis[u]+w;pre[v]=i;
                    if(!vis[v]){
                        qq.push(v);vis[v]=1;
                    }
                }
            }
        }
    }
    if(dis[t]==INF)return 0;
    return 1;
}

int cai[200][200];
int chushi[200];

int num=0;

int MC(){
    int ans=0,flow=0;
    while(1){
        num++;
        if(!SPFA(s))break;
        int tem=INF;
        for(int i=pre[t];i;i=pre[e[i].u]){
            tem=min(tem,e[i].w);
        }
        int t1=0;
        for(int i=pre[t];i;i=pre[e[i].u]){
            ans+=e[i].c*tem;
            e[i].w-=tem;e[i^1].w+=tem;
        }
        t1=e[pre[t]].u;
        t1-=n;
        int t2=t1,t3;
        t2%=m;//第几个厨师 
        if(!t2){
            t2=m;
            t3=t1/m-1;//前面的 
        }
        else t3=t1/m;
        flow+=tem;
        for(int i=1;i<=n;i++){
            add(i,n+(t3-1)*m+t2,1,cai[i][t2]*(tot-t3+1));
        }
        add(n+(t3-1)*m+t2,t,1,0);
    }
    return ans;
}

int tmp[MAXN];

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int tem;
        scanf("%d",&tmp[i]);
        tot+=tmp[i];
    }     //n+1->n+tot*m;厨师
    // 一个厨师是tot个点。每列编号递增 
    s=0,t=tot*m+n+1;
    for(int i=1;i<=n;i++)add(s,i,tmp[i],0);//1-n菜 
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&cai[i][j]);//第i个菜 第j厨师 
        }
    }
    for(int i=n+(tot-1)*m+1;i<=n+tot*m;i++){
        add(i,t,1,0);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            add(i,n+(tot-1)*m+j,1,cai[i][j]);
        }
    }
    printf("%d\n",MC()); 
    return 0;
} 

最小割

分治最小割、最小割树。用于统计两两点之间最小割。

任意选两个点,然后dfs找S集T集、统计S集,T集中点对最小割,在S集T集中分治。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;
const int INF=1e9+7;

inline int mymin(int x,int y){return x>y?y:x;}
int a[MAXN],n,m,s,t,ansflow[170][170];
bool markd[MAXN];

struct edge{
    int to,next,w;
}e[MAXN<<1];

int head[MAXN],cur[MAXN],cnt;

inline void add(int u,int v,int w){
    e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}

queue<int>q;
int dep[MAXN];
bool bfs(int x){
    memset(dep,0,sizeof(dep));
    q.push(x);dep[x]=1;
    while(q.size()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].w;
            if(w&&!dep[v]){
                dep[v]=dep[u]+1;q.push(v);
            }
        }
    }
    if(!dep[t])return 0;
    return 1;
}

int dfs(int u,int flow){
    if(u==t||flow==0)return flow;
    for(int &i=cur[u];i;i=e[i].next){
        int v=e[i].to,w=e[i].w;
        if(w&&dep[v]==dep[u]+1){
            int tem=dfs(v,mymin(w,flow));
            if(tem){
                e[i].w-=tem;
                e[i^1].w+=tem;
                return tem;
            }
        }
    }
    return 0;
}

void mem(){
    memset(e,0,sizeof(e));
    memset(a,0,sizeof(a));
    memset(head,0,sizeof(head));
    memset(ansflow,0x3f,sizeof(ansflow));
    cnt=1;
}

void re(){
    for(int i=2;i<=cnt;i+=2){
        e[i].w=e[i^1].w=((e[i].w+e[i^1].w)>>1);
    }
}

void color(int u){
    markd[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to,w=e[i].w;
        if(w&&!markd[v])color(v);
    }
}

int tmp[MAXN];

void solve(int l,int r){
    if(l==r)return;
    re();
    s=a[l],t=a[r];
    int ans=0;
    while(bfs(s)){
        for(int i=1;i<=n;i++)cur[i]=head[i];
        while(int d=dfs(s,INF))ans+=d; 
    }
    memset(markd,0,sizeof(markd));
    color(s);
    for(int i=1;i<=n;i++)
        if(markd[i])
        for(int j=1;j<=n;j++)
            if(!markd[j]){
                ansflow[i][j]=ansflow[j][i]=mymin(ans,ansflow[i][j]);
            }
    int L=l,R=r;
    for(int i=l;i<=r;i++){
        if(markd[a[i]])tmp[L++]=a[i];
        else tmp[R--]=a[i];
    }
    for(int i=l;i<=r;i++){
        a[i]=tmp[i];
    }
    solve(l,L-1);
    solve(R+1,r);
}

void work(){
    mem();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c);
    }
    for(int i=1;i<=n;i++)a[i]=i;
    int q;
    solve(1,n);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int x;
        scanf("%d",&x);
        int output=0;
        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++){
                if(ansflow[i][j]<=x)output++;
            }
        printf("%d\n",output);
    }
    printf("\n");
}

int main(){
    int num;
    scanf("%d",&num);
    while(num--)work();
    return 0;
} 

不同的最小割
这道题做法和ZJOI最小割是差不多的,如果数组开不够,用个set或map应该可以统计答案。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;
const int INF=1e9+7;

inline int mymin(int x,int y){return x>y?y:x;}

struct edge{
    int to,next,w;
}e[MAXN<<1];

int n,m,s,t;

int head[MAXN],cur[MAXN],cnt=1;
int ansflow[900][900];

inline void add(int u,int v,int w){
    e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}

queue<int>q;
int dep[MAXN];

bool bfs(int x){
    memset(dep,0,sizeof(dep));
    q.push(x);dep[x]=1;
    while(q.size()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].w;
            if(w&&!dep[v]){
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    if(!dep[t])return 0;
    return 1;
}

int dfs(int u,int flow){
    if(u==t||flow==0)return flow;
    for(int &i=cur[u];i;i=e[i].next){
        int v=e[i].to,w=e[i].w;
        if(w&&dep[v]==dep[u]+1){
            int tem=dfs(v,mymin(w,flow));
            if(tem){
                e[i].w-=tem;
                e[i^1].w+=tem;
                return tem;
            }
        }
    }
    return 0;
}

void re(){
    for(int i=2;i<=cnt;i+=2){
        e[i].w=e[i^1].w=(e[i].w+e[i^1].w)>>1;
    }
}

bool markd[MAXN];

void color(int u){
    markd[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to,w=e[i].w;
        if(w&&!markd[v])color(v);
    }
}

int tmp[1000],a[1000];

void solve(int l,int r){
    if(l==r)return;
    re();
    int ans=0;
    s=a[l],t=a[r];//原图里随便选两个点源汇 
    while(bfs(s)){
        for(int i=1;i<=n;i++)cur[i]=head[i];
        while(int d=dfs(s,INF))ans+=d;
    }
    memset(markd,0,sizeof(markd));
    color(s); 
    for(int i=1;i<=n;i++)if(markd[i])
    for(int j=1;j<=n;j++)if(!markd[j]){
        ansflow[i][j]=ansflow[j][i]=mymin(ansflow[i][j],ans);
    }
    int L=l,R=r;
    for(int i=l;i<=r;i++){
        if(markd[a[i]])tmp[L++]=a[i];//这个地方肯定要真实值。 
        else tmp[R--]=a[i];
    }
    for(int i=l;i<=r;i++)a[i]=tmp[i];
    solve(l,L-1);
    solve(R+1,r);
}

bool query[80000005];

int main(){
    memset(query,0,sizeof(query));
    memset(ansflow,0x3f,sizeof(ansflow));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1;i<=n;i++)a[i]=i;
    solve(1,n);
    int output=0;
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++){
            if(!query[ansflow[i][j]]){
                output++;
                query[ansflow[i][j]]=1; 
            }
        }
    printf("%d\n",output);
    return 0;
}

交换棋子
显然要拆点。
我们发现从起始交换到目标状态的路径上,中间交换次数为2,两端点为1。从i交换到j的总交换次数为(dis(i,j)-1)*2。
每个起始和目标状态点都可以少交换一次。
所以起始和目标状态点的限制次数+1.
容量可以设置为限制次数/2。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=2e5+5;
const int base=1e5;
const int INF=1e9+7;

inline int mymin(int x,int y){return x>y?y:x;}

int cnt=1,head[MAXN],n,m,s,t;

struct edge{
    int u,to,next,w,c;
}e[MAXN<<1];

inline void add(int u,int v,int w,int cost){
    e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;
    e[++cnt]=(edge){v,u,head[v],0,-cost},head[v]=cnt;
}

bool vis[MAXN];
int dis[MAXN],pre[MAXN];
queue<int>q;

bool SPFA(int x){
    memset(pre,0,sizeof(pre));
    for(int i=s;i<=t;i++)dis[i]=INF;
    q.push(x);dis[x]=0;vis[x]=1;
    while(q.size()){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].c;
            if(e[i].w&&dis[u]+w<dis[v]){
                dis[v]=dis[u]+w;pre[v]=i;
                if(!vis[v]){
                    vis[v]=1;q.push(v);
                }
            }
        }   
    }
    if(dis[t]==INF)return 0;
    return 1;
}
int flow=0;

int MC(){
    int ans=0;
    while(SPFA(s)){
        int tem=INF;
        for(int i=pre[t];i;i=pre[e[i].u]){
            tem=mymin(e[i].w,tem);
        }
        for(int i=pre[t];i;i=pre[e[i].u]){
            e[i].w-=tem;
            e[i^1].w+=tem;
            ans+=e[i].c*tem;
        }
        flow+=tem;
    }
    return ans;
}

char tem[MAXN];
int chushi[25][25],mubiao[25][25],xianzhi[25][25];

void init(){
    for(int i=1;i<=n;i++){
        scanf("%s",tem+1);
        for(int j=1;j<=m;j++)
        chushi[i][j]=tem[j]-'0';
    }
    for(int i=1;i<=n;i++){
        scanf("%s",tem+1);
        for(int j=1;j<=m;j++)
        mubiao[i][j]=tem[j]-'0';
    }
    for(int i=1;i<=n;i++){
        scanf("%s",tem+1);
        for(int j=1;j<=m;j++)
        xianzhi[i][j]=tem[j]-'0';
    }
}


int judge(int x,int y){//9*9有没有1
    if(chushi[x][y]==1&&mubiao[x][y]==1)return 2;
    if(chushi[x][y]==1||mubiao[x][y]==1)return 1;
    return 0;
}

int main(){
    int qizi=0;
    scanf("%d%d",&n,&m);
    init();
    s=0,t=n*m+base+1;
    for(int i=1;i<=n*m;i++){//i入  i+base出 
        int lie=i%m,hang;
        if(!lie) lie=m,hang=i/m;
        else hang=(i/m)+1;
        int p=judge(hang,lie);
        if(p==1)add(i,i+base,(xianzhi[hang][lie]+1)/2,0);
        else if(p==2)add(i,i+base,(xianzhi[hang][lie]+2)/2,0);
        else add(i,i+base,xianzhi[hang][lie]/2,0);
        if(hang!=1)add(i+base,i-m,INF,1);//上 
        if(hang!=n)add(i+base,i+m,INF,1);//下
        if(lie!=1)add(i+base,i-1,INF,1);//左
        if(lie!=m)add(i+base,i+1,INF,1);//右
        if(hang!=1&&lie!=1)add(i+base,i-m-1,INF,1);//上左 
        if(hang!=1&&lie!=m)add(i+base,i-m+1,INF,1);//上右
        if(hang!=n&&lie!=1)add(i+base,i+m-1,INF,1);//下左
        if(hang!=n&&lie!=m)add(i+base,i+m+1,INF,1);//下右 
        if(chushi[hang][lie]==1)add(s,i,1,0),qizi++;
        if(mubiao[hang][lie]==1)add(i+base,t,1,0);
    }   
    int ans=MC();
    if(qizi==flow)printf("%d\n",ans);
    else printf("-1\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值