题目:
题意:
对于给定的一张 n ∗ m n*m n∗m的图,有空地,敌人,以及导弹,每个导弹有指定的方向,只能打到对应方向上的一个位置,并且要求导弹弹道不相交的情况下,最多能消灭多少敌人
分析:
显然的,竖着的互不干扰,横着的也是,所以我们将整个图分开来了,所有点变成两个,一个竖点,一个横点,对应与横、竖弹道的关系
因为每个点不可能被同时打到,所以我们将其竖点和横点连一条无限大的边,每个导弹在其方向上,与最大贡献的位置连一条边,而所有方向向北或南的导弹向
S
S
S点连边,剩下导弹向另一点
T
T
T连边
现在如果不相交,那么就可以算答案了,可这很不实际,所以我们考虑在连好边的情况下,弹道相交是一种什么情况
相交,即有竖边和横边撞到一起了,形成了交点,因为在新图上,我们是分开考虑的,所以交点就意味着又变得要一起考虑,也就是
S
S
S和
T
T
T忽然连通了,这是我们不希望看到的
而我们之前导弹都是向最大贡献的点
(
v
m
a
x
)
(v_{max})
(vmax)连边,但如果出现冲突了,我们就需要换一个点
(
v
x
)
(v_x)
(vx),退而求其次,这样对于我们原本的答案来说就是
−
v
m
a
x
+
v
x
-v_{max}+v_x
−vmax+vx,我们在
x
x
x点和最大贡献点连一条权值为此的边,表示我们要删除这条边,而边权就是对答案造成的影响
因为是要求最大值,所以我们希望造成的影响尽量的小,也就是转化为求整个图的最小割了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
inline LL read()
{
LL s=0,f=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
return s*f;
}
int n,m,g[55][55];
int to[20005],nex[20005],b[20005],e=1,ls[5005];
int id(int x,int y,int z) {return (x-1)*m+y+(z?n*m:0);}
void add(int x,int y,int z)
{
to[++e]=y; nex[e]=ls[x]; ls[x]=e; b[e]=z;
to[++e]=x; nex[e]=ls[y]; ls[y]=e; b[e]=0;
return;
}
int mar[5005],ans,s,t,tot,bc[5005];
void build(int x,int y,int dir)
{
int mp=0,k=-1;
g[x][y]=0;
if(dir==-1)
{
add(s,id(x,y,0),inf);
for(int i=x-1;i>=1;--i)
if(g[i][y]>mp) mp=g[i][y],k=i;
if(k==-1) return ;
for(int i=x-1;i>=k;--i) add(id(i+1,y,0),id(i,y,0),mp-g[i+1][y]);
}
if(dir==-2)
{
add(s,id(x,y,0),inf);
for(int i=x+1;i<=n;++i)
if(g[i][y]>mp) mp=g[i][y],k=i;
if(k==-1) return ;
for(int i=x+1;i<=k;++i) add(id(i-1,y,0),id(i,y,0),mp-g[i-1][y]);
}
if(dir==-3)
{
add(id(x,y,1),t,inf);
for(int i=y-1;i>=1;--i)
if(g[x][i]>mp) mp=g[x][i],k=i;
if(k==-1) return ;
for(int i=y-1;i>=k;--i) add(id(x,i,1),id(x,i+1,1),mp-g[x][i+1]);
}
if(dir==-4)
{
add(id(x,y,1),t,inf);
for(int i=y+1;i<=m;++i)
if(g[x][i]>mp) mp=g[x][i],k=i;
if(k==-1) return ;
for(int i=y+1;i<=k;++i) add(id(x,i,1),id(x,i-1,1),mp-g[x][i-1]);
}
ans+=mp;
return;
}
bool bfs()
{
memset(mar,0,sizeof(mar));
queue<int> q;
mar[s]=1;
q.push(s);
while(q.size())
{
int x=q.front();
q.pop();
for(int i=ls[x];i;i=nex[i])
if(b[i]&&!mar[to[i]])
mar[to[i]]=mar[x]+1,q.push(to[i]);
}
return mar[t];
}
int dfs(int x,int maxflow)
{
if(x==t||!maxflow) return maxflow;
int res=0,f;
for(int &i=bc[x];i;i=nex[i])
if(b[i]&&mar[to[i]]==mar[x]+1)
{
f=dfs(to[i],min(maxflow,b[i]));
b[i]-=f;
b[i^1]+=f;
maxflow-=f;
res+=f;
if(!maxflow) break;
}
return res;
}
int Dinic()
{
int res=0;
while(bfs()) memcpy(bc,ls,sizeof(bc)),res+=dfs(s,inf);
return res;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
g[i][j]=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
add(id(i,j,0),id(i,j,1),inf);
s=n*m*2+1;t=n*m*2+2;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(g[i][j]<0) build(i,j,g[i][j]),tot++;
if(!tot) printf("0");
else cout<<ans-Dinic();
return 0;
}
博客围绕给定的n*m图,探讨在有空地、敌人和导弹,且导弹弹道不相交的情况下,最多能消灭多少敌人的问题。通过将图的点分为竖点和横点,构建边的连接关系,分析弹道相交情况,将问题转化为求整个图的最小割,并给出了解题思路。

被折叠的 条评论
为什么被折叠?



