Time Limit: 10 Sec
Memory Limit: 259 MB
Description
lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征 战来获得更大的领土。 A国是一
个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们
约定:
- 每支军 队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。
途中只能经过城镇,不能经过高山深涧。 - 如果某个城镇被某支军+队到过,则其他军队不能再去那个城镇了。
- 每支军 队都可以在任意一个城镇停止征战。
- 所有军 队都很奇怪,他们走的方法有点像国际象棋中的马。
不过马每次只能走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;
}