理 想 的 正 方 形 [ 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+n−1]中的最小和最大值。求出 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+n−1][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+n−1][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}{【中文题意】:}
【中文题意】:
【
分
析
】
:
\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
,但是在第一步交换1
和0
时,我们的程序认为它使得原字符串的字典序变大了,以至于没有交换而退出了程序。
所以,我们只能另寻思路。我们发现1
是可以到达天涯海角的,所以我们把1
全部提出来,剩下一个只有0
和2
的字符串,它是不可以交换的。但是我们可以把所有的1
全部插入第一个2
以前,这样得出来的答案一定是字典序最小的。
在我的第二个代码中用到了 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