社区讨论

站外题研究

学术版参与者 2已保存回复 6

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
6 条
当前快照
1 份
快照标识符
@lo13su04
此快照首次捕获于
2023/10/22 14:44
2 年前
此快照最后确认于
2023/11/02 14:15
2 年前
查看原帖

题目描述

出题人是一项开采项目的负责人。整个矿坑可以被看成一个 N×NN×N 的方阵,每个格子所代表的区域内有一些有矿,有的没有。
在进行开采之前,出题人只知道哪些格子有矿,而你需要帮他探明所有有矿的格子中的蕴藏量。
你有一个超能力,可以一次性探清 H×WH×W 的矩形中所有格子内的矿物蕴藏量;不过使用一次需要消耗 max(H,W)max(H,W) 的能量。
你需要在完成出题人给你的任务的情况下最小化消耗的能量并将这个数告诉出题人即可。

输入格式

输入文件名为
CPP
detection.in
第一行,一个整数 NN ,表示方阵的边长 。 接下来 NN行,每行一个字符串, NN 个字符串表示一张地图。其中 T 表示格子内有矿物。

输出格式

一行一个正整数表示答案。

数据范围

对于100%数据,N60N\le60

思路

dpi,jdp_{i,j}表示从(1,1)(1,1)探测到(i,j)(i,j)所需要的最小代价。我们枚举1i1i,1j1j1\le i_1 \le i,1\le j_1 \le j,则由容斥原理,有dpi,j=min(dpi,j,dpi11,j+dpi,j11+Cdp_{i,j}=min(dp_{i,j},dp_{i_1-1,j}+dp_{i,j_1-1}+C 若以(i1,j1)(i_1,j_1)为左上角,(i,j)(i,j)为右下角的长方形中有需要探测的点,则C=max(ii1+1,jj1+1)C=max(i-i_1+1,j-j_1+1),反之则C=0C=0
下面我们来考虑如何快速判断一个长方形内有无需要探测的点。将需要探测的点赋值为1,不需要的赋值为0,再做二维前缀和即可。
以下是代码:
CPP
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;
char map[65][65];
int dp[65][65];
int cnt[65][65];
int main(){
	//freopen("detection2.in","r",stdin);
	//freopen("detection2.out","w",stdout);
	int n;
	cin>>n;
	if(n==1){
		char c;
		cin>>c;
		if(c=='T') cout<<1<<endl;
		else cout<<0<<endl;
		return 0;
	}
	int cnt_t=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			cin>>map[i][j];
			if(map[i][j]=='T'){
				cnt[i][j]=1;
				cnt_t++;
			} 
		} 
	if(cnt_t<=2){
		cout<<cnt_t<<endl;
		return 0;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) cnt[i][j]=cnt[i][j-1]+cnt[i-1][j]-cnt[i-1][j-1]+cnt[i][j];
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++) dp[i][0]=dp[0][i]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			for(int i1=1;i1<=i;i1++)
				for(int j1=1;j1<=j;j1++){
					bool tflag=1;
					if(dp[i][j]==7) tflag=0;
					int t=cnt[i][j]-cnt[i][j1-1]-cnt[i1-1][j]+cnt[i1-1][j1-1];
					dp[i][j]=min(dp[i][j],dp[i][j1-1]+dp[i1-1][j]-dp[i1-1][j1-1]+(t>0?max(i-i1+1,j-j1+1):0));
					if(i==8 and j==5 and dp[i][j]==7 and tflag){
						cout<<i1<<" "<<j1<<endl;
						cout<<t<<endl;
						cout<<dp[i][j1-1]<<" "<<dp[i1-1][j]<<" "<<dp[i1-1][j1-1]<<" "<<max(i-i1+1,j-j1+1)<<endl;
					} 
				}
		}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) cout<<setw(1)<<dp[i][j];
		cout<<endl;
	} 
	cout<<dp[n][n]<<endl;
}
该代码在原OJ上AC 链接:https://www.xinyoudui.com/contest?courses=685&books=676&pages=19969&fragments=63899&problemId=19036

hack

经过同机房大佬对拍,发现一组可以hack本代码的数据:
CPP
10
TOOOOOOOOO
OOOOOOOOOO
OOTTOOTOOO
TOOOOOOOOO
OTOOOOOOOO
OOOOTOOOOO
OOOTOOOTOT
OOTOOOOOOO
OOTOOTOOOO
OOOOOOOOOO
代码输出9,而答案应为10。所以该思路到底有什么问题,希望各位大佬能予以解答。

回复

6 条回复,欢迎继续交流。

正在加载回复...