BZOJ4657 : tower

显然只有横向和纵向的两个炮塔才有可能冲突。

考虑最小割,将每个炮塔所有能攻击到的位置建点,相邻之间连无穷的边,表示前缀和关系,即选了一个点,就必须要选所有比它近的点。

属于横向炮塔的点向$S$连边,容量为前缀最大值的差值;属于纵向炮塔的点向$T$连边,容量为前缀最大值的差值。

对于一个交点,则在两个点之间连无穷边,表示必须舍弃其中一个。

答案$=$总收益$-$最小割。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=126000,inf=~0U>>2;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
struct E{int t,f;E*nxt,*pair;}*g[N],*d[N],pool[1000000],*cur=pool;
int n,m,i,j,a[55][55],b[55][55],cnt,S,T,h[N],gap[N],ans;
inline void add(int s,int t,int f){
  if(!f)return;
  if(f<inf)ans+=f;
  E*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p;
  p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p;
  g[s]->pair=g[t];g[t]->pair=g[s];
}
int sap(int v,int flow){
  if(v==T)return flow;
  int rec=0;
  for(E*p=d[v];p;p=p->nxt)if(h[v]==h[p->t]+1&&p->f){
    int ret=sap(p->t,min(flow-rec,p->f));
    p->f-=ret;p->pair->f+=ret;d[v]=p;
    if((rec+=ret)==flow)return flow;
  }
  if(!(--gap[h[v]]))h[S]=T;
  gap[++h[v]]++;d[v]=g[v];
  return rec;
}
inline void tag(int x,int y,int k){
  int mx=0,pre=0,now;
  while(1){
    x+=dx[k],y+=dy[k];
    if(x<1||x>n||y<1||y>m)return;
    cnt++;
    if(pre){
      if(k<2)add(cnt,pre,inf);
      else add(pre,cnt,inf);
    }
    if(k<2)add(S,cnt,max(mx,a[x][y])-mx);else add(cnt,T,max(mx,a[x][y])-mx);
    mx=max(mx,a[x][y]);
    pre=cnt;
    if(b[x][y]){
      if(k<2)add(cnt,b[x][y],inf);
      else add(b[x][y],cnt,inf);
    }else b[x][y]=cnt;
  }
}
int main(){
  scanf("%d%d",&n,&m);
  S=n*m*max(n,m)+1;T=S+1;
  for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&a[i][j]);
  for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(a[i][j]<0)tag(i,j,a[i][j]+4);
  for(gap[0]=T,i=1;i<=T;i++)d[i]=g[i];
  while(h[S]<T)ans-=sap(S,inf);
  return printf("%d",ans),0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/5723755.html