2020.01.18日常总结

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

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

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

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

我们记 p m i n [ i ] [ j ] pmin[i][j] p m a x [ 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 m i n pmin p m a x pmax 后,记 f m i n [ 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] 中的最小值, f m a x [ 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] 中的最大值。

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

总的时间复杂度是 O ( a × b ) O(a \times b) 。代码中的定义稍有不同,在代码中也有解释(其实就是把 f m i n [ i ] [ j ] , f m a x [ i ] [ j ] fmin[i][j],fmax[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}

\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 ,这里解释一下一些函数,大家可以通过类比的思想知道它们的用途:

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

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

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

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

输入:2010,输出:1200

发布了82 篇原创文章 · 获赞 4 · 访问量 1762

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/104029836