bzoj 4657: tower 最小割

    xiaoxiao2021-04-12  31

    题意

    Nick最近在玩一款很好玩的游戏,游戏规则是这样的: 有一个n*m的地图,地图上的每一个位置要么是空地,要么是炮塔,要么是一些BETA狗,Nick需 要操纵炮塔攻击BETA狗们。 攻击方法是:对于每个炮塔,游戏系统已经给出它可以瞄准的方向(上下左右其中一个),Nick需要 选择它的攻击位置,每一个炮塔只能够攻击一个位置,炮塔只能够向着它的瞄准方向上的某个位置发 动攻击,当然炮塔也可以不进行攻击。炮塔威力强大,它可以且仅可以消灭目标位置上所有的BETA狗。 出于安全考虑,游戏系统已经保证不存在一个炮塔能够瞄准另外一个炮塔,即对于任意一个炮 塔,它所有可能的攻击位置上不存在另外一个炮塔。而且,如果把炮塔的起点和终点称为炮弹的运行 轨迹,那么系统不允许两条轨迹相交f包括起点和终点)。 现在,选定目标位置以后,每一个炮塔同时开炮,你要告诉Nick,他最多可以干掉多少BETA狗。

    第一行两个正整数n,m,表示地图的规模。 接下来礼行,每行m个整数,0表示空地,-1,-2,一3,-4分别表示瞄准上下左右的炮塔,若为正整 数p,则表示该位置有p个BETA狗。 n,m <= 50,每个位置的BETA狗数量不超过999个,保证不存在任意一个炮塔能够瞄准另外一个炮塔

    分析

    一开始想的是费用流,就是直接把上下相邻和左右相邻的点连边,然后每个点向t连一条边,然后跑了一下发现样例错了,调了一下发现炮弹居然会转弯……简直神了。。。

    考虑最小割,我们可以先把每个炮台能打到的范围内的最大值找出来,加入答案。 考虑一个横着的炮塔和一个竖着的炮塔,他们有一个交点, 如果我们把一个炮塔到他要打的地方这一条路径中两两相邻的点连边,那么显然不能让两个炮塔之间有路径。 那么我们可以将每个点拆成两个点,一个表示横着走的点,称为横点;一个表示竖着走的点,称为竖点。每个点从竖点到横点连inf的边。 从s到每个竖着的炮台连inf的边,从每个横着的炮台到t连inf的边。 把每个竖着的炮台到其最大值的格子的路径上两两相邻的竖点连边,方向为炮塔打的方向,流量为最大值减去边的起点所在的格子的权值。也就是说若我们割了一条u->v的边,就相当于我们打u这个点(可以是自己)。 把每个横着的炮台到其最大值的格子的路径上两两相邻的横点连边,方向为炮塔的反方向,流量为最大值减去边的终点所在的格子的权值。也就是说若我们割了一条u->v的边,就相当于我们打v这个点。 那么由于s集和t集不能连通,所以这两个炮塔的路径上一定会有一条边被割掉。 于是我们用最大值的和减去最小割就是答案了。

    感觉题解比代码还难打2333

    代码

    #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=55; const int M=N*N*2; const int inf=0x3f3f3f3f; int n,m,map[N][N],cnt,dis[M],last[M],dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},cur[M],s,t,ans; struct edge{int to,c,next;}e[M*5]; queue <int> q; int point(int x,int y,int z) { if (z==1) return (x-1)*m+y; else return n*m+(x-1)*m+y; } void addedge(int u,int v,int c) { e[++cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].c=0;e[cnt].next=last[v];last[v]=cnt; } bool bfs() { for (int i=s;i<=t;i++) dis[i]=0; dis[s]=1; while (!q.empty()) q.pop(); q.push(s); while (!q.empty()) { int u=q.front(); q.pop(); for (int i=last[u];i;i=e[i].next) if (e[i].c&&!dis[e[i].to]) { dis[e[i].to]=dis[u]+1; if (e[i].to==t) return 1; q.push(e[i].to); } } return 0; } int dfs(int x,int maxf) { if (x==t||!maxf) return maxf; int ret=0; for (int &i=cur[x];i;i=e[i].next) if (e[i].c&&dis[e[i].to]==dis[x]+1) { int f=dfs(e[i].to,min(e[i].c,maxf-ret)); e[i].c-=f; e[i^1].c+=f; ret+=f; if (maxf==ret) break; } return ret; } void dinic() { while (bfs()) { for (int i=s;i<=t;i++) cur[i]=last[i]; ans-=dfs(s,inf); } } int main() { freopen("tower.in","r",stdin);freopen("tower.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&map[i][j]); s=0;t=n*m*2+1;cnt=1; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (map[i][j]<0) { int mx=0; int p=i,q=j,op=-map[i][j]-1; while (1) { p+=dx[op];q+=dy[op]; if (p<1||p>n||q<1||q>m) break; mx=max(mx,map[p][q]); } ans+=mx; if (op<2) addedge(s,point(i,j,1),inf); else addedge(point(i,j,2),t,inf); p=i;q=j; while (1) { int lp=p,lq=q; p+=dx[op];q+=dy[op]; if (p<1||p>n||q<1||q>m) break; if (op<2) addedge(point(lp,lq,1),point(p,q,1),mx-max(0,map[lp][lq])); else addedge(point(p,q,2),point(lp,lq,2),mx-max(map[lp][lq],0)); } } else addedge(point(i,j,1),point(i,j,2),inf); dinic(); printf("%d",ans); return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-667898.html

    最新回复(0)