2020.01.18日常总结

理 想 的 正 方 形 [ H A O I 2007 ] \color{green}{理想的正方形[HAOI2007]} [HAOI2007]

【 题 意 】 : \color{blue}{【题意】:}
理想的正方形——题意
【 思 路 】 : \color{blue}{【思路】:} 首先考虑暴力的做法——很简单,枚举正方形的左上角,然后用 打 擂 台 \color{red}{打擂台} 的方法求出最大值和最小值更新答案即可。

然而这样做的时间复杂度是 O ( a × b × n 2 ) O(a \times b \times n^2) O(a×b×n2),一定会超时。

考虑如何优化, O ( a × b ) O(a \times b) O(a×b)是底线,只能想方设法把 n 2 n^2 n2省略掉。

我们记 p m i n [ i ] [ j ] pmin[i][j] pmin[i][j] p m a x [ i ] [ j ] pmax[i][j] pmax[i][j]分别表示 p [ i ] [ j ] , p [ i ] [ j + 1 ] , p [ i ] [ j + 2 ] . . . p [ i ] [ j + n − 1 ] p[i][j],p[i][j+1],p[i][j+2]...p[i][j+n-1] p[i][j],p[i][j+1],p[i][j+2]...p[i][j+n1]中的最小和最大值。求出 p m i n pmin pmin p m a x pmax pmax后,记 f m i n [ i ] [ j ] fmin[i][j] fmin[i][j]表示 p m i n [ i ] [ j ] , p m i n [ i + 1 ] [ j ] , p m i n [ i + 2 ] [ j ] . . . p m i n [ i + n − 1 ] [ j ] pmin[i][j],pmin[i+1][j],pmin[i+2][j]...pmin[i+n-1][j] pmin[i][j],pmin[i+1][j],pmin[i+2][j]...pmin[i+n1][j]中的最小值, f m a x [ i ] [ j ] fmax[i][j] fmax[i][j]表示 p m a x [ i ] [ j ] , p m a x [ i + 1 ] [ j ] , p m a x [ i + 2 ] [ j ] . . . p m a x [ i + n − 1 ] [ j ] pmax[i][j],pmax[i+1][j],pmax[i+2][j]...pmax[i+n-1][j] pmax[i][j],pmax[i+1][j],pmax[i+2][j]...pmax[i+n1][j]中的最大值。

我们可以发现 p m i n , p m a x , f m i n , f m a x pmin,pmax,fmin,fmax pmin,pmax,fmin,fmax都可以用 O ( a × b ) O(a \times b) O(a×b)的算法求出,而 f m i n [ i ] [ j ] , f m a x [ i ] [ j ] fmin[i][j],fmax[i][j] fmin[i][j],fmax[i][j]就是以 ( i , j ) (i,j) (i,j)为左上角的正方形的最小最大值。

总的时间复杂度是 O ( a × b ) O(a \times b) O(a×b)。代码中的定义稍有不同,在代码中也有解释(其实就是把 f m i n [ i ] [ j ] , f m a x [ i ] [ j ] fmin[i][j],fmax[i][j] fmin[i][j],fmax[i][j]变成了以 ( i , j ) (i,j) (i,j)为右下角的正方形的最小最大值而已)。

【 代 码 】 : \color{blue}{【代码】:}

const int N=1020;
int P_min[N][N],P_max[N][N];
int F_min[N][N],F_max[N][N];
int ans,n,m,k,q[N],l,r,a[N][N];
//P_min[i][j]=min{a[i][v](j-k+1<=v<=j)}
//P_max[i][j]=max{a[i][v](j-k+1<=v<=j)}
//F_max[i][j]=max{P_max[v][j](i-k+1<=v<=i)}
//F_min[i][j]=min{P_min[v][j](i-k+1<=v<=i)}
int main(){
//	freopen("t1.in","r",stdin);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++){
		memset(q,0,sizeof(q));l=1;r=0;
		for(int j=1;j<=m;j++){
			while (l<=r&&q[l]<=j-k) l++;
			while (l<=r&&a[i][q[r]]>=a[i][j]) r--;
			q[++r]=j;if (j>=k) P_min[i][j]=a[i][q[l]];
		}
		memset(q,0,sizeof(q));l=1;r=0;
		for(int j=1;j<=m;j++){
			while (l<=r&&q[l]<=j-k) l++;
			while (l<=r&&a[i][q[r]]<=a[i][j]) r--;
			q[++r]=j;if (j>=k) P_max[i][j]=a[i][q[l]];
		}
	}
	for(int j=k;j<=m;j++){
		memset(q,0,sizeof(q));l=1;r=0;
		for(int i=1;i<=n;i++){
			while (l<=r&&q[l]<=i-k) l++;
			while (l<=r&&P_min[q[r]][j]>=P_min[i][j]) r--;
			q[++r]=i;if (i>=k) F_min[i][j]=P_min[q[l]][j];
		}
		memset(q,0,sizeof(q));l=1;r=0;
		for(int i=1;i<=n;i++){
			while (l<=r&&q[l]<=i-k) l++;
			while (l<=r&&P_max[q[r]][j]<=P_max[i][j]) r--;
			q[++r]=i;if (i>=k) F_max[i][j]=P_max[q[l]][j];
		}
	}
	ans=0x3f3f3f3f;
	for(int i=k;i<=n;i++)
		for(int j=k;j<=m;j++)
			ans=min(ans,F_max[i][j]-F_min[i][j]);
	printf("%d\n",ans);
	return 0;
}

M i n i m u m     T e r n a r y     S t r i n g \color{green}{Minimum\ \ \ Ternary\ \ \ String} Minimum   Ternary   String

【 英 文 题 意 】 : \color{blue}{【英文题意】:}
英文题意
【 中 文 题 意 】 : \color{blue}{【中文题意】:}
中文题意——1
中文题意——2
【 分 析 】 : \color{blue}{【分析】:}
首先我们来看一个贪心的思路:只要交换后能使原字符串的字典序更小,我们就交换,直到不能交换为止,比如这样:

const int N=1e5+100;
char s[N];int n;bool flag;
int main(){
// 	freopen("t1.in","r",stdin);
	scanf("%s",s+1);
	n=strlen(s+1);
	do{
		flag=false;
		for(int i=1;i<n;i++)
			if (s[i]-s[i+1]==1){
				swap(s[i],s[i+1]);
				flag=true;
			}
	}while(flag);
	printf("%s",s+1);
	return 0;
}

然而这样做是有反例的:

比如原字符串是201,我们通过肉眼的模拟可以知道答案应该是120,但是在第一步交换10时,我们的程序认为它使得原字符串的字典序变大了,以至于没有交换而退出了程序。

所以,我们只能另寻思路。我们发现1是可以到达天涯海角的,所以我们把1全部提出来,剩下一个只有02的字符串,它是不可以交换的。但是我们可以把所有的1全部插入第一个2以前,这样得出来的答案一定是字典序最小的。

可AC代码

在我的第二个代码中用到了 S T L STL STL,这里解释一下一些函数,大家可以通过类比的思想知道它们的用途:

  • s.find('2'):在字符串 s s s中寻找第一个2的位置,若没有,则返回string::npos,即s.npos

  • s.insert(l,s1):在 s s s的第 l l l个字符前插入 s 1 s1 s1

  • s+s1,s+r[i]:第一个语句相当于把 s 1 s1 s1插入在 s s s的末尾,第二个语句相当于把 r [ i ] r[i] r[i]插入在 s s s的末尾,由此可见,如果要把一个字符串或字符插入在另一个字符串的末尾,就可以使用字符串的加法运算。但请注意,单单写s+s1并没有完成真正的插入操作,s=s+s1才完成了插入的操作。

最后,我给大家一个第一个程序无法通过但第二个程序可以通过的样例:

输入:2010,输出:1200

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值