【BZOJ】1066: [SCOI2007]蜥蜴
【算法】网络流-最大流(dinic)
【题解】
构图思路:
因为石柱高度是可以被消耗的,即一根石柱可通过的蜥蜴数量有限,取舍问题中这样表示容量的属性显然可以作为网络流中的边。
于是将一根石柱拆成顶部和底部,中间连一条容量为石柱高度的边。
超级源向有蜥蜴的石柱顶连一条容量为1的边(表示一只蜥蜴)。
可以跳出地图的石柱底向超级汇连一条容量为inf的边。
d距离内的两根石柱连一条容量为inf的边。
记得结果要的是逃不掉的蜥蜴数。
edge数组开大!!!
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=1000,inf=0x3f3f3f3f; struct edge{int from,v,flow;}e[500000]; int n,m,d,first[maxn],cur[maxn],q[510],p[30][30],dt[maxn],S=0,T=801,tot=1,ans=0; void insert(int u,int v,int w) {tot++;e[tot].v=v;e[tot].flow=w;e[tot].from=first[u];first[u]=tot; tot++;e[tot].v=u;e[tot].flow=0;e[tot].from=first[v];first[v]=tot;} bool bfs() { memset(dt,-1,sizeof(dt)); int head=0,tail=1;q[0]=S;dt[S]=0; while(head!=tail) { int x=q[head++];if(head>=501)head=0; for(int i=first[x];i;i=e[i].from) if(dt[e[i].v]==-1&&e[i].flow>0) { q[tail++]=e[i].v;if(tail>=501)tail=0; dt[e[i].v]=dt[x]+1; } } if(dt[T]==-1)return 0; return 1; } int dinic(int x,int a) { if(x==T||a==0)return a; int flow=0,f; for(int& i=cur[x];i;i=e[i].from) if(dt[e[i].v]==dt[x]+1&&(f=dinic(e[i].v,min(a,e[i].flow)))>0) { e[i].flow-=f; e[i^1].flow+=f;//tot从2开始!建反向弧! flow+=f; a-=f; if(a==0)break; } return flow; } int main() { scanf("%d%d%d",&n,&m,&d); int cnt=0; char s[30]; for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) { cnt++;int u; p[i][j]=cnt; u=s[j]-'0'; if(u)insert(cnt,cnt+400,u); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int x=max(i-d,1);x<=min(i+d,n);x++) for(int y=max(j-d,1);y<=min(j+d,m);y++) if((i!=x||j!=y)&&(i-x)*(i-x)+(j-y)*(j-y)<=(d*d)) insert(p[i][j]+400,p[x][y],inf); for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) if(s[j]=='L')insert(S,p[i][j],1),ans++; } for(int i=d+1;i<=n-d;i++) for(int j=1;j<=d;j++) { insert(p[i][j]+400,T,inf); insert(p[i][m-j+1]+400,T,inf); } for(int i=1;i<=d;i++) for(int j=1;j<=m;j++) { insert(p[i][j]+400,T,inf); insert(p[n-i+1][j]+400,T,inf); } while(bfs()) { for(int i=0;i<=801;i++)cur[i]=first[i]; ans-=dinic(S,inf); } printf("%d",ans); return 0; }