[2018.3.25集訓]cti-最小割
阿新 • • 發佈:2018-03-27
來源 所有 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-最小割