China Final J

和一般的管道不同

不能类似“无限之环”或者“弯弯国”的建图,因为这两个题都是某些位置必须有,或者必须没有

但是本题可以有的位置随意,不能限制某个位置要么流2,要么流0,(实际上可能流了1过去)

 所以建图方式不能一样了。

唯一的好处是:只有四种管道。

横的、竖的,所以考虑拆点

法一:

黑白染色

每个点拆成两个点,横、竖

黑色:横->竖,竖->上下的白点的竖,左右白点的横->横

白色:竖->横,横->到左右黑点的横,上下的黑点->竖

必须的就上下界[1,1]否则[0,1]

也即形如:

无源汇上下界最大费用可行流。

求出可行流,在第二步增广的时候,费用<0就break掉 

直接跑最大费用最大流得到可行流即可。

PS:可能有正环,需要消圈。[学习笔记]费用流

法二:

考虑连成的若干个环

黑白染色

黑色:横向的是出边,纵向的是入边

白色:纵向的是出边,横向的是入边

然后类似“星际竞速”就可以了

如果一个边不需要选择,就入点直接向出点连边。

最大费用最大流。

之所以可以用“星际竞速”建图来做,因为每个边可以有固定的流向。

且一定一个边入,一个边出,最终成环

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
');}
namespace Modulo{
const int mod=998244353;
int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
void inc(int &x,int y){x=ad(x,y);}
int mul(int x,int y){return (ll)x*y%mod;}
void inc2(int &x,int y){x=mul(x,y);}
int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);}
template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);}
}
// using namespace Modulo;
namespace Miracle{
const int N=40;
const int inf=0x3f3f3f3f;
const int P=2*N*N;
int n,m;
struct node{
    int nxt,to;
    int w,c;
}e[20*(N*N*2*2)];
int hd[P],cnt=1;
int num(int i,int j,int t){
    return t*n*m+(i-1)*m+j;
}
void add(int x,int y,int w,int c){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;e[cnt].w=w;e[cnt].c=c;
    hd[x]=cnt;

    e[++cnt].nxt=hd[y];
    e[cnt].to=x;e[cnt].w=0;e[cnt].c=-c;
    hd[y]=cnt;
}
int dis[P],vis[P];
int incf[P],pre[P];
bool lim[N][N];
int s,t;
queue<int>q;
int ans,flow;
bool spfa(){
    memset(dis,0xcf,sizeof dis);
    dis[s]=0;incf[s]=inf;
    pre[t]=0;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        vis[x]=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(e[i].w&&dis[y]<dis[x]+e[i].c){
                dis[y]=dis[x]+e[i].c;
                pre[y]=i;incf[y]=min(incf[x],e[i].w);
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
    if(!pre[t]) return false;
    return true;
}
void upda(){
    int x=t;
    while(x!=s){
        e[pre[x]].w-=incf[t];
        e[pre[x]^1].w+=incf[t];
        x=e[pre[x]^1].to;
    }
    ans+=dis[t]*incf[t];
    flow+=incf[t];
}
void clear(){
    memset(hd,0,sizeof hd);
    cnt=1;
    ans=0;flow=0;
    s=0;t=0;
    memset(lim,0,sizeof lim);
}
int main(){
    int T;
    rd(T);
    for(reg o=1;o<=T;++o){
        rd(n);rd(m);
        clear();
        s=0;t=num(n,m,1)+1;
        int v;
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<m;++j){
                rd(v);
                if((i+j)&1){//white
                    add(num(i,j+1,0),num(i,j,1),1,v);
                }else{//black
                    add(num(i,j,0),num(i,j+1,1),1,v);
                }
            }
        }
        for(reg i=1;i<n;++i){
            for(reg j=1;j<=m;++j){
                rd(v);
                if((i+j)&1){//white
                    add(num(i,j,0),num(i+1,j,1),1,v);
                }else{
                    add(num(i+1,j,0),num(i,j,1),1,v);
                }
            }
        }
        int k;
        rd(k);
        int x,y;
        for(reg i=1;i<=k;++i){
            rd(x);rd(y);
            lim[x][y]=1;
        }
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                add(s,num(i,j,0),1,0);
                add(num(i,j,1),t,1,0);
                if(!lim[i][j]) add(num(i,j,0),num(i,j,1),1,0);
            }
        }
        while(spfa()) upda();
        printf("Case #%d: ",o);
        if(flow==n*m){
            printf("%d
",ans);
        }else{
            printf("Impossible
");
        }
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

其实两个方法有点类似

都是相当于黑白染色,然后给边定向

法一,通过循环流直接就是环

法二,“星际竞速”图,可以拼成环

原文地址:https://www.cnblogs.com/Miracevin/p/11004587.html