题目
有一个n*m的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭敌人。 对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,当然也可以不进行攻击。一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭。 保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔。 定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮弹轨迹相交。
解题思路
条件性质
考虑题目的关键信息有什么性质。 ①要求消灭的敌人最多。 ②如果选择了某个区域(下文将“区域”说成“链”),那么另一些区域不能选。并且不能选的条件当且仅当两链相交。 ③链的相交处不可能为炮塔所在地。 ④同为横或同为竖的两个炮塔不会互相有影响。
整体感知
感觉上这题很棘手,因为改变一条链与其他的链的长短是否改变有很大的关系。 而且每条链的长短不同与其他的链的长短是否改变有很大的关系。 所以可以考虑网络流模型。 要考虑消灭敌人最多,并且这些冲突的链要有取舍(选择性),所以选择最小割模型。
建图
图中的什么东西表达了横链和纵链有冲突?在最小割模型中,
S
S
与TT点连通是不是说明了这个事? 那么有什么东西可以将炮塔分为两部分?显然是横和竖。将点拆成横点和竖点。待会讲为什么。每个竖点向对应的横点连一条边,流量为Inf。 所以建立源点,连向所有的竖炮的竖点。所有横炮的横点连向汇点。流量为Inf。 那么一条链怎么表示? 首先对于每一个炮,我们只需要知道它的最远攻击的点一定是这个炮能够打的敌人最多的点。(这不是很显然吗)设原计划为每个炮能够打到它攻击范围内敌人最多的点。设这个值为
Max(i)
M
a
x
(
i
)
由于每一条链的具体长度都会影响到其他链的长度,所以横链指向与自己相邻的左/右的横点连边,竖链指向与自己相邻的上/下的竖点连边。假设S与T连通,那么说明两条链中间会有交点,说明按照原计划算答案肯定有冲突。 那么考虑最小割,割去表示不要。设割去的边连着的点为
u
u
,这个点上有map[u]map[u]个敌人,那么流量为
Max(i)−map[u]
M
a
x
(
i
)
−
m
a
p
[
u
]
。 然后跑一遍就好了。答案为
Sum−最小割
S
u
m
−
最
小
割
。
抓关键
①最小割模型明显有一种事物分为2个集合。 ②割表示不要,什么是全部贡献,什么是割去的贡献。
代码
using namespace std;
struct note{
int to,
next,flow;
};note edge[
2000010];
int tot,head[M];
int height[M];
int qu[
2000010];
int i,j,k,l,n,
m,ans;
int map[N][N];
int S,T;
int read(){
int fh=
1,rs=
0;char ch;
while((ch<
'0'||ch>
'9')&&(ch^
'-'))ch=getchar();
if(ch==
'-')fh=-
1,ch=getchar();
while(ch>=
'0'&&ch<=
'9')rs=(rs<<
3)+(rs<<
1)+(ch^
'0'),ch=getchar();
return fh
*rs;
}
int id(
int x,
int y,
int z){
return (
x-
1)
*m+
y+(z?n
*m:
0);}
void lb(
int x,
int y,
int z){
edge[++tot].to=
y;edge[tot].
next=head[
x];edge[tot].flow=z;head[
x]=tot;
edge[++tot].to=
x;edge[tot].
next=head[
y];edge[tot].flow=
0;head[
y]=tot;
}
void build(
int x,
int y,
int p){
map[
x][
y]=
0;
int i,j,mx=
0,wz=
0;
if(p==
1){
lb(S,id(
x,
y,
0),Inf);
fd(i,
x-
1,
1)
if(
map[i][
y]>mx)mx=
map[i][
y],wz=i;
if(!wz)
return;
fd(i,
x-
1,wz)lb(id(i+
1,
y,
0),id(i,
y,
0),mx-
map[i+
1][
y]);
}
if(p==
2){
lb(S,id(
x,
y,
0),Inf);
fo(i,
x+
1,n)
if(
map[i][
y]>mx)mx=
map[i][
y],wz=i;
if(!wz)
return;
fo(i,
x+
1,wz)lb(id(i-
1,
y,
0),id(i,
y,
0),mx-
map[i-
1][
y]);
}
if(p==
3){
lb(id(
x,
y,
1),T,Inf);
fd(i,
y-
1,
1)
if(
map[
x][i]>mx)mx=
map[
x][i],wz=i;
if(!wz)
return;
fd(i,
y-
1,wz)lb(id(
x,i,
1),id(
x,i+
1,
1),mx-
map[
x][i+
1]);
}
if(p==
4){
lb(id(
x,
y,
1),T,Inf);
fo(i,
y+
1,
m)
if(
map[
x][i]>mx)mx=
map[
x][i],wz=i;
if(!wz)
return;
fo(i,
y+
1,wz)lb(id(
x,i,
1),id(
x,i-
1,
1),mx-
map[
x][i-
1]);
}
ans+=mx;
}
bool BFS(){
memset(height,-
1,sizeof(height));
height[S]=
0;
int i,j,
x,l,r;
l=
0,r=
1;
qu[r]=S;
while(l<r){
x=qu[++l];
for(i=head[
x];i;i=edge[i].
next)
if(edge[i].flow>
0&&height[edge[i].to]==-
1){
height[edge[i].to]=height[
x]+
1;
qu[++r]=edge[i].to;
}
}
return ~height[T]?
1:
0;
}
int Min(
int x,
int y){
return x<
y?
x:
y;}
int find(
int u,
int thin){
if(u==T)
return thin;
int a,i,sum=
0;
for(i=head[u];i;i=edge[i].
next)
if(height[edge[i].to]==height[u]+
1&&edge[i].flow>
0){
a=find(edge[i].to,Min(edge[i].flow,thin));
if(a>
0){
sum+=a;
thin-=a;
edge[i].flow-=a;
edge[i^
1].flow+=a;
if(!thin)
return sum;
}
}
if(!sum)height[u]=-
1;
return sum;
}
int main(){
freopen(
"cti.in",
"r",stdin);
freopen(
"cti.out",
"w",stdout);
tot=
1;
n=
read();
m=
read();
S=
0,T=
2*n*m+
1;
fo(i,
1,n)fo(j,
1,
m)
map[i][j]=
read();
fo(i,
1,n)fo(j,
1,
m)lb(id(i,j,
0),id(i,j,
1),Inf);
fo(i,
1,n)fo(j,
1,
m)
if(
map[i][j]<
0)build(i,j,-
map[i][j]);
while(BFS())
ans-=find(S,Inf);
printf(
"%d",ans);
return 0;
}