题目描述 Description
这个月的pku月赛某陈没有参加,因为当时学校在考试[某陈经常逃课,但某陈还没有强大到考试也可以逃掉的程度].何况,对于北大校赛,水牛通常是没有什么希望考得好的[事实上某陈最好成绩是仅A了一道题].
某陈郁闷.接下来他又将沉浸在无穷尽的刷题中,每天面对各种颜色的Status--WA,TLE,RE,甚至还有MLE,CE,PE什么什么的,他无比期待蓝色的AC.
话说RP爆发的某陈弄到了很久以后某次pku月赛的某题的题目和输入数据,如下所示.
输入数据包括n个测试点,每个点都有一个最大可获得的分数m.选手需要选定任意3个整数,i,j,m0,1<=i<=j<=n,代表选手选择的测试点范围是从第i个到第j个,每个测试点的期望分数均为m0.本题为Special Judge[特殊评测],评测时系统将遍历标号从i到j的测试点,对于被遍历的每一个测试点,如果当前测试点的m小于m0,则终止评测并判定选手得分为0,否则系统将为选手得分加上m0[系统初始化选手得分为0].若最终选手得分与可能的最大得分相同,那么选手就AC了这题.
某陈已经为本题写了好久的代码,现在他需要知道本题可能的最大得分,来验证他的输出是否为最优解.请计算选手的最大得分,给某陈一个打表的机会~
[某陈:神啊..请赐予我力量吧..我需要一次华丽丽的AC..]
输入描述 Input Description
输入文件第一行为一个整数n,如题目描述中的含义.0<n<=10^6
接下来的一行包括n个整数m,如题目描述中的含义.0<=m<2^31
输出描述 Output Description
输出文件包含一个整数,为题目描述中的最大得分.
force1:暴力枚举法,O(n^2),不过显然过不了。
2:单调栈:首先来说一说单调栈
单调栈与单调队列很相似。首先栈是后进先出的,单调性指的是严格的递增或者递减。
单调栈有以下两个性质:
1、若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严格递减的。
2、越靠近栈顶的元素越后进栈。
单调栈与单调队列不同的地方在于栈只能在栈顶操作,因此一般在应用单调栈的地方不限定它的大小,否则会造成元素无法进栈。
元素进栈过程:对于单调递增栈,若当前进栈元素为e,从栈顶开始遍历元素,把小于e或者等于e的元素弹出栈,直接遇到一个大于e的元素或者栈为空为止,然后再把e压入栈中。对于单调递减栈,则每次弹出的是大于e或者等于e的元素。
一个单调递增栈的例子:
进栈元素分别为3,4,2,6,4,5,2,3
3进栈:(3)
3出栈,4进栈:(4)
2进栈:(4,2)
2出栈,4出栈,6进栈:(6)
4进栈:(6,4)
4出栈,5进栈:(6,5)
2进栈:(6,5,2)
2出栈,3进栈:(6,5,3)
以上左端为栈底,右端为栈顶。(摘自http://blog.csdn.net/alongela/article/details/8227707)
之后这题就可以用单调栈来做啦。
然后就是对于比栈顶元素大的,我们就直接入栈。
对于比栈顶元素小的,我们把比它小的元素出栈,且把最大值更新((当前元素下标-出栈元素进栈时间)*出栈元素),最后进栈, 进栈时间设为最后一个元素出栈的时间(自己思考为什么- -)。
O(n)一遍过。。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 8 int stack[1000001],top=0,time[1000001]; 9 10 int main() 11 { 12 int n,x; 13 long long mx=0; 14 scanf("%d",&n); 15 for(int i=1;i<=n+1;i++) 16 { 17 int d=i; 18 if(i<=n)scanf("%d",&x); 19 else x=-1; 20 if(!top)stack[++top]=x,time[top]=1; 21 while(x<stack[top]&&top!=0) 22 { 23 long long sb=(long long)stack[top]*(i-time[top]); 24 mx=max(mx,sb); 25 d=time[top]; 26 top--; 27 } 28 if(x>stack[top])stack[++top]=x,time[top]=d; 29 } 30 printf("%lld",mx); 31 return 0; 32 }
题目描述 Description
在一个0,1方阵中找出其中最大的全0子矩阵,所谓最大是指O的个数最多。
输入描述 Input Description
输入文件第一行为整数N,其中1<=N<=2000,为方阵的大小,紧接着N行每行均有N个0或1,相邻两数间严格用一个空格隔开。
输出描述 Output Description
输出文件仅一行包含一个整数表示要求的最大的全零子矩阵中零的个数。
force1:枚举每个矩阵的左上右下两点,判断里面的元素是否全为0,复杂度0(n6),可以加个最优型剪枝加速一下。。
2.首先我们用f[i][j]来表示第i行第j列以上的元素有几个0.
然后我们发现,这不就是在每一列求第一题吗。。
好了。。就是这样,记得当g[i][j]为1是f[i][j]=0,
f[i][j]可以优化成1维的!
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 using namespace std; 7 8 int num[2001],stack[2001],time[2001],top=0,ans=0,n; 9 10 bool g[2001]; 11 12 void work() 13 { 14 top=0; 15 memset(stack,0,sizeof(stack)); 16 memset(time,0,sizeof(time)); 17 for(int i=1;i<=n+1;i++) 18 { 19 int d=i,x=num[i]; 20 if(i==n+1)x=-1; 21 if(!top)stack[++top]=x,time[1]=1; 22 while( stack[top]>x && top!=0 ) 23 { 24 int sb=stack[top]*(i-time[top]); 25 ans=max(ans,sb); 26 d=time[top]; 27 top--; 28 } 29 if(stack[top]<x||x==0)stack[++top]=x,time[top]=d; 30 } 31 } 32 33 int main() 34 { 35 int x; 36 scanf("%d",&n); 37 for(int i=1;i<=n;i++) 38 { 39 for(int j=1;j<=n;j++) 40 { 41 scanf("%d",&x); 42 g[j]=x; 43 if(!g[j])num[j]++; 44 else num[j]=0; 45 } 46 work(); 47 } 48 printf("%d",ans); 49 return 0; 50 }
第三题题目我手贱删了。。
讲讲大致题意把。。
就是说给你一个矩阵,有各种数字,求满足每个矩阵的左上加右上小于等于左下加右上且它的子矩阵也满足条件的矩阵的最大面积,矩阵的最小长,宽为2.
force1及优化就不说了。。
正解:把所有的2*2矩阵缩成一个小矩阵。。满足条件就是0,不满足就是1.(根据个人喜好)
然后就是喜闻乐见的第二题解法了。。记得答案是(当前元素下标-出栈元素进栈时间+1)*(出栈元素+1)。。
代码也删了。。大家自己脑补吧。。