您的位置:首页 > 理论基础 > 计算机网络

bzoj1066(网络流)

2016-07-28 17:42 603 查看
 在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃
到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石
柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不
变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个
石柱上。

不是很难的建模,就是注意加边的时候一些细节,不要加重复也不要少加

一个点的出点要连上它所能到达的点的入点,注意这里并不是只能往下和右走,而是可以向四个方向走,那么就是要向四个方向都连,//就是wa在这里了

a的出点连b的入点,b的出点连a的入点,实际上既然可以向四个方向走,那么就可以向四个方向都连,每一个点

注:如果题目允许向各个方向(向右向下,向右向上,向左向下,向左向上)走,那么就是每个点向各个方向连,每个点只连单向的四种,别的点也连这样的就成一对了,就覆盖所有情况了

如果只是向右和向下走,那么一个点只与它下面和右边的点连,只有一种情况

(嗷嗷嗷,好乱好乱~)

总之,关于这个棋盘里面连边方向的问题,需要仔细想一想,确保考虑好所有的情况

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<iostream>
#define debug(x) cout<<#x<<"="<<endl
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1009;
const int len[5]={0,2,5,10,14};//对于每一个d,所能连的最大限度
const int qx[14]={-1,0,0,-1,-2,0,-1,-2,-3,-2,0,-1,-3,-4};
const int qy[14]={0,-1,-2,-1,0,-3,-2,-1,0,-2,-4,-3,-1,0};//距离范围内所能到达的点坐标差,然后向四个方向连
int r,c,d,s,t;
int id1[22][22],id2[22][22],map[22][22];
int ok(int x,int y)
{
if (x<1||x>r||y<1||y>c) return 1;
if (map[x][y]==0) return 2;
return 3;
}
int lev
,head
,tot,cur
;
struct aa
{
int cap,to,pre,flow;
}edge[50009];
void addedge(int x,int y,int z)
{
edge[++tot].to=y;edge[tot].cap=z;edge[tot].pre=head[x];head[x]=tot;
edge[++tot].to=x;edge[tot].cap=0;edge[tot].pre=head[y];head[y]=tot;
}
bool bfs()
{
memset(lev,0,sizeof(lev));
queue<int> q;q.push(s);lev[s]=1;
while (!q.empty())
{
int u=q.front();q.pop();
for (int i=head[u];i;i=edge[i].pre)
if (edge[i].cap>edge[i].flow&&lev[edge[i].to]==0)
{
lev[edge[i].to]=lev[u]+1;
if (edge[i].to==t) return true;
q.push(edge[i].to);
}
}
return false;
}
int dfs(int u,int maxflow)
{
if (u==t||maxflow==0) return maxflow;
int ans=0;
for (int &i=cur[u];i;i=edge[i].pre)
if (lev[edge[i].to]==lev[u]+1)
{
int flow=dfs(edge[i].to,min(maxflow,edge[i].cap-edge[i].flow));
ans+=flow;
maxflow-=flow;
edge[i].flow+=flow;
edge[((i-1)^1)+1].flow-=flow;
if (maxflow==0) return ans;
}
return ans;
}
int work()
{
int ans=0;
while (bfs())
{
for (int i=s;i<=t;i++) cur[i]=head[i];
ans+=dfs(s,inf);
}
return ans;
}
int main()
{
scanf("%d%d%d",&r,&c,&d);
int bj=0,total=0;
for (int i=1;i<=r;i++)
for (int j=1;j<=c;j++)
id1[i][j]=++bj,id2[i][j]=++bj;
char ch[30];
s=0,t=bj+1;
for (int i=1;i<=r;i++)
{
scanf("%s",ch);
for (int j=0;j<strlen(ch);j++)
{
if (ch[j]>'0') addedge(id1[i][j+1],id2[i][j+1],ch[j]-'0');
map[i][j+1]=ch[j]-'0';
}
}
for (int i=1;i<=r;i++)
{
scanf("%s",ch);
for (int j=0;j<strlen(ch);j++)
if (ch[j]=='L') addedge(s,id1[i][j+1],1),total++;
}
for (int i=1;i<=r;i++)
for (int j=1;j<=c;j++)
{
bool o=false;
for (int l=0;l<len[d];l++)
{
int x=ok(i+qx[l],j+qy[l]);
if (x==1) o=true;
if (x==3) addedge(id2[i][j],id1[i+qx[l]][j+qy[l]],inf);
if (qy[l]!=0)
{
x=ok(i+qx[l],j-qy[l]);
if (x==1) o=true;
if (x==3) addedge(id2[i][j],id1[i+qx[l]][j-qy[l]],inf);
}
if (qx[l]!=0)//注意特殊情况的处理,就是如果在x坐标相同,就不能通过这个找对称点,他的对称点就是原来这个点
{
x=ok(i-qx[l],j+qy[l]);
if (x==1) o=true;
if (x==3) addedge(id2[i][j],id1[i-qx[l]][j+qy[l]],inf);
}
if (qx[l]!=0&&qy[l]!=0)
{
x=ok(i-qx[l],j-qy[l]);
if (x==1) o=true;
if (x==3) addedge(id2[i][j],id1[i-qx[l]][j-qy[l]],inf);
}
}
if (o) addedge(id2[i][j],t,inf);
}

printf("%d",total-work());
return 0;
}

总结

1:在棋盘问题中,关于根据移动方向连边的问题需要仔细地想一想,保证可以连的那些方向的边可以不重不漏。

2:这道题就是很经典的网络流,一个流代表一个方案(一个流代表一个青蛙跑出去),且有一些约束,一个石头只能到几次,在通过各种技巧来进行处理这些约束就可以了。(这里限制流过几只青蛙,是通过分点,连边容量为限制量的技巧来处理的,也是很经典)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: