[unknown OJ] T形覆盖

博客探讨了一道名为[T形覆盖]的问题,通过部分分的思路,分析了当max(xi−xj,yi−yj)为1或2时的不同情况。在情况1中,两点形成九宫格,覆盖相同;情况2中,可选择一个点空出,减少重合。博主指出重复格子数量(A)与关键点数量(B)的关系,并提供了A=B和A=B-1两种情况下如何选择格子的方法,最后给出以dfs实现的解决方案,时间复杂度为O(nm)。" 112082314,10542130,JAVA语言的锁内部实现与dicom信息读取,"['JAVA并发', '锁机制', '并发编程', 'JAVA内核']

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

一、题目

点此看题

二、解法

根据部分分的提示,我们可以想到如果 max ⁡ ( x i − x j , y i − y j ) ≥ 2 \max(x_i-x_j,y_i-y_j)\geq2 max(xixj,yiyj)2,那么这两个点是毫无瓜葛的,我们可以继续用这种方法来分类讨论所有情况。

情况 1 1 1:如果 max ⁡ ( x i − x j , y i − y j ) = 1 \max(x_i-x_j,y_i-y_j)=1 max(xixj,yiyj)=1,这两个点是在九宫格内的,看图:
在这里插入图片描述

不难发现就算有两种选择他们覆盖的点都是一样的,而且十字架重合的部分是 2 2 2 个格子。

情况 2 2 2:如果 max ⁡ ( x i − x j , y i − y j ) = 2 \max(x_i-x_j,y_i-y_j)=2 max(xixj,yiyj)=2,可以选择十字架区域的任意一个点空出来,并且这个情况十字架重合的格子是 1 1 1 个。

设重复的个数是 A A A,一定区域内选择会相互影响的关键点个数是 B B B,不难发现有 A ≤ B A\leq B AB,因为重复的地方都要求某个点留一个空给他,这样的机会是 B B B次的。然后因为这些关键点会互相影响,所以 A ≥ B − 1 A\geq B-1 AB1,最少一个重复的格子可以连接两个关键点。这样一来, A A A的取值就很少了。

  • 如果 A = B A=B A=B,那么这些涉及到的格子都要选。
  • 如果 A = B − 1 A=B-1 A=B1,那么是可以随性所欲地空出来一个格子的,空出来权值最小的格子即可。

所以实现的时候以 d f s \tt dfs dfs 的形式写,时间复杂度 O ( n m ) O(nm) O(nm)

#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,ans,vis[M],fl[M];vector<int> a[M];
int val,dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int dfs(int x,int y,int &val)
{
	vis[x*m+y]=1;
	val++;
	if(x==0 || x==n-1) val--;//这里"机会"会被消耗 
	if(y==0 || y==m-1) val--;
	if(val<0) return -1;
	ans+=a[x][y];
	int Mi=1005;
	//九宫格内的情况 
	for(int i=x-1;i<=x+1;i++)
		for(int j=y-1;j<=y+1;j++)
		{
			if(i<0 || j<0 || i>=n || j>=m) continue;
			if(fl[i*m+j] && !vis[i*m+j])
			{
				ans-=a[x][j];
				ans-=a[i][y];
				int res=dfs(i,j,val-=2);
				if(res==-1) return -1;
				Mi=min(Mi,res);
			}
		}
	//外面的四个特殊点 
	for(int d=0;d<4;d++)
	{
		int i=x+dx[d],j=y+dy[d];
		if(i<0 || j<0 || i>=n || j>=m) continue;
		ans+=a[i][j];Mi=min(Mi,a[i][j]);
		i+=dx[d];j+=dy[d];
		if(i<0 || j<0 || i>=n || j>=m || !fl[i*m+j]) continue;
		if(x*m+y<i*m+j)//小->大,只算一次
		{
			val--;
			ans-=a[(i+x)/2][(j+y)/2];
		}
		if(vis[i*m+j])
		{
			if(val<0) return -1;
			continue;
		}
		int res=dfs(i,j,val);
		if(res==-1) return -1;
		Mi=min(Mi,res);
	}
	return Mi;
}
int main()
{
	n=read();m=read();
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			a[i].push_back(read());
	k=read();
	for(int i=1;i<=k;i++)
	{
		int x=read(),y=read();
		fl[x*m+y]=1;
	}
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(!vis[i*m+j] && fl[i*m+j])
			{
				val=0;int res=dfs(i,j,val);
				if(res==-1)
				{
					puts("No");
					return 0;
				}
				if(val) ans-=res;
			}
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值