1. 程式人生 > >[2018.3.25集訓]cti-最小割

[2018.3.25集訓]cti-最小割

來源 所有 its AC 最小 ++ int mark out

題目大意

給定一個$n*m$的網格圖上
有些格子內存在炮臺,每個炮臺都被指定了上下左右四個方向中的某一個方向,並可以選定這個方向上的一個格子發動一次攻擊。
保證沒有任何炮臺能攻擊另一個炮臺,同時炮臺可以不攻擊。
有些格子內存在數量為$a[i][j]$的敵人,攻擊一個格子能擊殺所有格子內的敵人。
定義每個炮臺與它攻擊的目標之間的格子為攻擊軌跡。一個合法的攻擊方案滿足,任意兩個炮臺之間的攻擊軌跡不相交。
求擊殺敵人數最多的一種合法方案擊殺的敵人數。

$n,m \leq 50,a[i][j] \leq 1000$

題解

當你做一道題,發現什麽都想不到的時候,可以試一試網絡流。-——出題人

考慮將格子上的權值取負,使用最小割模型。

假設不考慮攻擊軌跡不相交的限制,那麽可以用如下方法建圖:

將每個格子拆成兩個點,分別為橫點和豎點。
對於每個橫著的炮臺,使源點連向它,將其覆蓋的格子的橫點串成一條鏈,流量為每個格子的敵人數目,最後令鏈上最後一個邊界格子連向匯點。
對於每個豎著的炮臺,使它連向匯點,同樣將覆蓋的格子串成一條鏈,但是使用的是豎點,且方向與橫著時相反,並令源點向鏈的起點,也就是邊界格子連邊。
這樣,建出的圖的一個割對應一組攻擊方案。

考慮不能相交的限制。

考慮在對應相交位置處的橫點向豎點連一條$Inf$邊。
此時,若橫點割掉相交處的格子對應的邊,則豎點割掉相交處格子及之前的邊並不能使圖不連通,因為橫著的$Inf$邊依舊可以流入。

於是,豎點只能割掉之後的邊。由於豎點的邊是反著建的,因此剩余可選擇的邊集對應原圖上不相交的所有方案。

同理,若豎點割掉相交處的格子對應的邊,則橫點只能割掉相交處的格子對應的邊之前的邊,否則之間的邊處的流量會通過$Inf$邊流向豎點的匯點。

這個模型的來源是HNOI的切糕一題。
於是跑一邊最小割即可~

代碼:
(-1、-2、-3、-4分別對應上下左右)

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'
|| '9'<ch){if(ch=='-')f=-1;ch=getchar();} while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar(); return x*f; } const int N=59; const int mint=1000; int n,m,s,t; int g[N][N],h[N][N]; int dx[]={0,0,1,-1}; int dy[]={1,-1,0,0}; inline bool in(int x,int y) { return 1<=x && x<=n && 1<=y && y<=m; } namespace flow { const int P=N*N*5; const int M=P*3; int to[M<<1],nxt[M<<1],w[M<<1],beg[P],tot=1; int dis[P],q[P]; inline void adde(int u,int v,int c) { to[++tot]=v; nxt[tot]=beg[u]; w[tot]=c; beg[u]=tot; } inline void add(int u,int v,int c) { //printf("%d %d %d\n",u,v,mint-c); adde(u,v,c);adde(v,u,0); } inline bool bfs() { memset(dis,-1,sizeof(dis)); q[1]=s;dis[s]=0; for(int l=1,r=1,u=q[l];l<=r;u=q[++l]) for(int i=beg[u];i;i=nxt[i]) if(w[i]>0 && !(~dis[to[i]])) dis[to[i]]=dis[u]+1,q[++r]=to[i]; return ~dis[t]; } inline int dfs(int u,int flow) { if(u==t || !flow)return flow; int cost=0; for(int i=beg[u],f;i;i=nxt[i]) if(w[i]>0 && dis[to[i]]==dis[u]+1) { f=dfs(to[i],min(flow-cost,w[i])); w[i]-=f;w[i^1]+=f;cost+=f; if(cost==flow)break; } if(!cost)dis[u]=-1; return cost; } inline int dinic() { int ret=0; while(bfs()) ret+=dfs(s,2e9); return ret; } } inline int pt(int a,int b,int d) { return (((a-1)*m+b)<<1)-d; } int main() { freopen("cti.in","r",stdin); freopen("cti.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) g[i][j]=read(); int totcnt=0; s=n*m*2+1;t=s+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(g[i][j]<=-3) { flow::add(s,pt(i,j,1),mint); int dir=g[i][j]+4,k; for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++) { int cx=i+k*dx[dir],cy=j+k*dy[dir]; int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir]; flow::add(pt(lx,ly,1),pt(cx,cy,1),mint-max(0,g[cx][cy])); h[cx][cy]=dir+1; } k--; int cx=i+k*dx[dir],cy=j+k*dy[dir]; flow::add(pt(cx,cy,1),t,1e8); totcnt++; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(-2<=g[i][j] && g[i][j]<=-1) { flow::add(pt(i,j,0),t,mint); int dir=g[i][j]+4,k; for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++) { int cx=i+k*dx[dir],cy=j+k*dy[dir]; int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir]; flow::add(pt(cx,cy,0),pt(lx,ly,0),mint-max(0,g[cx][cy])); if(h[cx][cy]) flow::add(pt(cx-dx[h[cx][cy]-1],cy-dy[h[cx][cy]-1],1),pt(lx,ly,0),1e8); } k--; int cx=i+k*dx[dir],cy=j+k*dy[dir]; flow::add(s,pt(cx,cy,0),1e8); totcnt++; } printf("%d\n",totcnt*mint-flow::dinic()); return 0; }

[2018.3.25集訓]cti-最小割