BZOJ2150 || 洛谷P2172 [国家集训队]部落战争【有上下界最小流 or DAG最小路径覆盖】

Time Limit: 10 Sec
Memory Limit: 259 MB

Description

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征 战来获得更大的领土。 A国是一
个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们
约定:

  1. 每支军 队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。
    途中只能经过城镇,不能经过高山深涧。
  2. 如果某个城镇被某支军+队到过,则其他军队不能再去那个城镇了。
  3. 每支军 队都可以在任意一个城镇停止征战。
  4. 所有军 队都很奇怪,他们走的方法有点像国际象棋中的马。
    不过马每次只能走12的路线,而他们只能走RC的路线。
    lanzerb的野心使得他的目标是统一全国,但是兵 力的限制使得他们在配备人手时力不从心。假设他们每支军队都
    能顺利占领这支军 队经过的所有城镇,请你帮lanzerb算算至少要多少支军 队才能完成统一全国的大业。
Input

第一行包含4个整数M、N、R、C,意义见问题描述。
接下来M行每行一个长度为N的字符串。
如果某个字符是’.’,表示这个地方是城镇;如果这个字符时’x’,表示这个地方是高山深涧。
1<=M,N<=50,1<=R,C<=10。

Output

输出一个整数,表示最少的军 队个数


解法一:有上下界最小流

每个城镇拆成入点和出点,由其入点向出点连边,下界1,上界1,表示必须且只能经过一次
对于u能到达的城镇v,u的出点向v的入点连边,下界0,上界inf
虚拟源点s向每个入点连边,下界0,上界inf
每个出点向虚拟汇点t连边,下界0,上界inf

按照有上下界网络流套路建图后求s到t的最小流即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int inf=1e9;
const int maxn=100010;
int n,m,r,c;
struct node{int v,f,nxt;}E[maxn<<1];
int head[maxn],tot=1;
int lev[maxn],deg[maxn];
int rem[55][55];
char ss[110];
int nX[]={1,1},nY[]={-1,1};

void add(int u,int v,int f)
{
    E[++tot].nxt=head[u];
    E[tot].v=v; E[tot].f=f;
    head[u]=tot;
    
    E[++tot].nxt=head[v];
    E[tot].v=u; E[tot].f=0;
    head[v]=tot;
}

int bfs(int s,int t)
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t) return 1;
                q.push(v);
            }
        }
    }
    return 0;
}

int dfs(int u,int cap,int t)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(lev[v]==lev[u]+1&&E[i].f&&flow)
        {
            int f=dfs(v,min(E[i].f,flow),t);
            E[i].f-=f; E[i^1].f+=f;
            flow-=f;
        }
    }
    return cap-flow;
}

int dicnic(int s,int t)
{
    int maxf=0;
    while(bfs(s,t)) maxf+=dfs(s,inf,t);
    return maxf;
}

int main()
{
    n=read();m=read();
    r=read();c=read();
    
    for(int i=1;i<=n;++i)
    {
        scanf("%s",&ss);
        for(int j=0;j<m;++j)
        if(ss[j]=='x') rem[i][j+1]=1;
    }
    
    int s=n*m*2+1,t=s+1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        if(rem[i][j]) continue;
        int u=(i-1)*m+j;
        add(s,u,inf); add(u+n*m,t,inf);//l=0,r=inf
        
        add(u,u+n*m,0);//l=1,r=1
        deg[u]--; deg[u+n*m]++;
    }
    
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        if(rem[i][j]) continue;
        int u=(i-1)*m+j;
        for(int k=0;k<2;++k)
        {
            int x=i+nX[k]*r,y=j+nY[k]*c,v=(x-1)*m+y;
            if(x>=1&&x<=n&&y>=1&&y<=m) 
            if(!rem[x][y]) add(u+n*m,v,inf);
        }
        if(r!=c)
        for(int k=0;k<2;++k)
        {
            int x=i+nX[k]*c,y=j+nY[k]*r,v=(x-1)*m+y;
            if(x>=1&&x<=n&&y>=1&&y<=m) 
            if(!rem[x][y]) add(u+n*m,v,inf);
        }
    }
    
    int ss=t+1,tt=ss+1;
    for(int i=1;i<=t;++i)
    {
        if(deg[i]>0) add(ss,i,deg[i]);
        else if(deg[i]<0) add(i,tt,-deg[i]);
    }
    add(t,s,inf);
    
    dicnic(ss,tt);
    int ans=E[tot].f;
    head[t]=E[head[t]].nxt;
    head[s]=E[head[s]].nxt;
    printf("%d",ans-dicnic(t,s));
    return 0;
}

解法二:DAG最小路径覆盖

有题意知所有可能的道路连接起来组成的一定是一个有向无环图
又有每个点必须且只能经过一次
这恰好对应DAG最小路径覆盖

依然是每个城镇拆成入点和出点,u到v的道路即连接u的入点到v的出点
城镇总数-最大匹配数即为答案

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int inf=1e9;
const int maxn=100010;
int n,m,r,c;
struct node{int v,f,nxt;}E[maxn<<1];
int head[maxn],tot=1;
int lev[maxn],deg[maxn];
int rem[55][55],ans;
char ss[110];
int nX[]={1,1},nY[]={-1,1};
 
void add(int u,int v,int f)
{
    E[++tot].nxt=head[u];
    E[tot].v=v; E[tot].f=f;
    head[u]=tot;
     
    E[++tot].nxt=head[v];
    E[tot].v=u; E[tot].f=0;
    head[v]=tot;
}
 
int bfs(int s,int t)
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t) return 1;
                q.push(v);
            }
        }
    }
    return 0;
}
 
int dfs(int u,int cap,int t)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(lev[v]==lev[u]+1&&E[i].f&&flow)
        {
            int f=dfs(v,min(E[i].f,flow),t);
            E[i].f-=f; E[i^1].f+=f;
            flow-=f;
        }
    }
    return cap-flow;
}
 
int dicnic(int s,int t)
{
    int maxf=0;
    while(bfs(s,t)) maxf+=dfs(s,inf,t);
    return maxf;
}
 
int main()
{
    n=read();m=read();
    r=read();c=read();
     
    ans=n*m;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",&ss);
        for(int j=0;j<m;++j)
        if(ss[j]=='x') rem[i][j+1]=1,ans--;
    }
     
    int s=n*m*2+1,t=s+1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        if(rem[i][j]) continue;
        int u=(i-1)*m+j;
        add(s,u,1); add(u+n*m,t,1);
    }
     
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        if(rem[i][j]) continue;
        int u=(i-1)*m+j;
        for(int k=0;k<2;++k)
        {
            int x=i+nX[k]*r,y=j+nY[k]*c,v=(x-1)*m+y;
            if(x>=1&&x<=n&&y>=1&&y<=m) 
            if(!rem[x][y]) add(u,v+n*m,1);
        }
        if(r!=c)
        for(int k=0;k<2;++k)
        {
            int x=i+nX[k]*c,y=j+nY[k]*r,v=(x-1)*m+y;
            if(x>=1&&x<=n&&y>=1&&y<=m) 
            if(!rem[x][y]) add(u,v+n*m,1);
        }
    }
    printf("%d",ans-dicnic(s,t));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值