校内测 U142303 不稳定的导弹系统【最小割】

博客围绕给定的n*m图,探讨在有空地、敌人和导弹,且导弹弹道不相交的情况下,最多能消灭多少敌人的问题。通过将图的点分为竖点和横点,构建边的连接关系,分析弹道相交情况,将问题转化为求整个图的最小割,并给出了解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目:

传送门


题意:

对于给定的一张 n ∗ m n*m nm的图,有空地,敌人,以及导弹,每个导弹有指定的方向,只能打到对应方向上的一个位置,并且要求导弹弹道不相交的情况下,最多能消灭多少敌人


分析:

显然的,竖着的互不干扰,横着的也是,所以我们将整个图分开来了,所有点变成两个,一个竖点,一个横点,对应与横、竖弹道的关系
因为每个点不可能被同时打到,所以我们将其竖点和横点连一条无限大的边,每个导弹在其方向上,与最大贡献的位置连一条边,而所有方向向北或南的导弹向 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值