zoukankan      html  css  js  c++  java
  • Week5 HomeWork

    A-最大矩形

    给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。 

    Input输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, ..., hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。Output对于每组测试数据输出一行一个整数表示答案。Sample Input

    7 2 1 4 5 1 3 3
    4 1000 1000 1000 1000
    0

    Sample Output

    8
    4000

    思路分析:
      要找到最大的矩形,我们可以把每个可能位最大矩形的结果算出来,然后挑选出来最大的那个矩形就是我们所得答案
      本题主要的思路是使用单调栈的思路解决的。单调栈就是单调+栈的意思。顾名思义,就是维护一个单调的栈。
      要想算出最大的矩形面积,我们可以将每个点的左右区间算出来,计算每个区间中的矩形面积,求出最大值即可。利用单调栈,我们可以求出每个点对应的一个端点,我们以fuc1()为例,数组st【】和top维护了一个单调栈结构,我们从左边看是将每个元素依次入栈,同时为了保持这个元素单调递增的特性,我们将不符合要求的元素从这个栈里面pop()出来,所以一旦有元素小于前一个元素的高度,那么前一个元素就会被pop出来,这个被pop出来的元素的右端点就是那个小于他的元素的上一个,如此便可以得出所有元素的右端点。同理,进行fuc2()即可得出每个元素的左右区间,然后计算出最大矩形即可。


     1 #include<iostream>
     2 using namespace std;
     3 
     4 long long l[100000];
     5 long long r[100000];
     6 long long p[100000];
     7 int st[100000];
     8 
     9 fuc1(int n){
    10     int top=0;
    11     st[top]=0;
    12     top++;
    13     for(int i=1;i<n;i++){    
    14         while(top!=0&&p[i]<p[st[top-1]]){
    15             l[st[top-1]]=i;
    16             top--;
    17         }
    18 //        if(top==0){
    19 //            st[top]=i;
    20 //            top++;
    21 //        }
    22         st[top]=i;
    23         top++;
    24     }
    25     for(int i=0;i<top;i++){
    26         l[st[i]]=n;
    27     }
    28 }
    29 
    30 void fuc2(int n){
    31     int top=0;
    32     st[top]=n-1;
    33     top++;
    34     for(int i=n-2;i>=0;i--){    
    35         while(top!=0&&p[i]<p[st[top-1]]){
    36             r[st[top-1]]=i+1;
    37             top--;
    38         }
    39 //        if(top==0){
    40 //            st[top]=i;
    41 //            top++;
    42 //        }
    43         st[top]=i;
    44         top++;
    45     }
    46     for(int i=0;i<top;i++){
    47         r[st[i]]=0;
    48     }
    49 }
    50 
    51 void cal(int n){
    52     long long ans=0;
    53     for(int i=0;i<n;i++){
    54         ans=max(ans,(l[i]-r[i])*p[i]);
    55 //cout<<l[i]<<" r"<<r[i]<<" p"<<p[i]<<" "<<(l[i]-r[i])*p[i]<<endl;
    56     }
    57     cout<<ans<<endl;
    58 }
    59 
    60 int main(){
    61     int n;
    62     scanf("%d",&n);
    63     while(n!=0){
    64         for(int i=0;i<n;i++){
    65             scanf("%lld",&p[i]);
    66         }
    67         fuc1(n);
    68 //        for(int i=0;i<n;i++){
    69 //            cout<<p[i]<<" "<<r[i]<<endl;
    70 //        }
    71         fuc2(n);
    72         cal(n);
    73         scanf("%d",&n);
    74     }
    75 }


    B-TT的魔法猫

    Thanks to everyone's help last week, TT finally got a cute cat. But what TT didn't expect is that this is a magic cat.

    One day, the magic cat decided to investigate TT's ability by giving a problem to him. That is select nncities from the world map, and a[i]a[i] represents the asset value owned by the ii-th city.

    Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r][l,r]and increase their asset value by cc. And finally, it is required to give the asset value of each city after qqoperations.

    Could you help TT find the answer?

    Input

    The first line contains two integers n,qn,q (1n,q2105)(1≤n,q≤2·105) — the number of cities and operations.

    The second line contains elements of the sequence aa: integer numbers a1,a2,...,ana1,a2,...,an (106ai106)(−106≤ai≤106).

    Then qq lines follow, each line represents an operation. The ii-th line contains three integers l,rl,r and c(1lrn,105c105)(1≤l≤r≤n,−105≤c≤105) for the ii-th operation.

    Output

    Print nn integers a1,a2,,ana1,a2,…,an one per line, and aiai should be equal to the final asset value of the ii-th city.

    Examples

    Input
    4 2
    -3 6 8 4
    4 4 -2
    3 3 1
    
    Output
    -3 6 9 2
    
    Input
    2 1
    5 -2
    1 2 4
    
    Output
    9 2
    
    Input
    1 2
    0
    1 1 -8
    1 1 -6
    
    Output
    -14

    思路分析:    
      这道题给我印象比较深的是数据表示的范围问题,之前,没有太注意过这方面,但是在这道题上,我把应该用long long表示的数据用了int来表示,然后一直是Wrong Answer,看了很久的代码也发现不了问题,费了很大的无用功,后来发现只是数据类型有问题了。int的表示范围一般不超过10^9,有了这次的教训,我以后看到10^4的数据就要考虑用long long了,这个的确是一个值得注意的问题。说回这道题目,这道题考察我们差分算法,本题并不难,但是直接暴力操作的话肯定会超时的,题目的本意就是让我们简化复杂度,那么我们就可以考虑,每次操作都是对于一个连续的区间,一个一个的操作太浪费时间了,我们主要的优化就是在这里。如果我们把原数组看作位数列的前n项和,那就可以看出,一个区间的取值只要改动两个数,一个是区间开始的前一个数,一个是后一个数。然后再用求n项和的方式就可以得出原来的数列了,这样有效的缩减了区间内的重复加减操作,在数据量足够大的时候有很大的时间复杂度优势。以下为源代码:

     1 #include<iostream>
     2 using namespace std;
     3 //一定要学会看数据范围,搞了半天才发现用int存数字范围小了,换成long long就好了 
     4 int main(){
     5     int n,q;
     6     cin>>n>>q;
     7     long long int *s,*a;
     8     s=new long long int[n+2];
     9     a=new long long int[n+2];
    10     s[0]=0;
    11     for(int i=1;i<=n;i++){
    12         cin>>s[i];
    13         a[i]=s[i]-s[i-1];
    14     }
    15     int l,r,c;
    16     for(int cnt=0;cnt<q;cnt++){
    17         cin>>l>>r>>c;
    18         a[l]+=c;
    19         if(r!=n){
    20             a[r+1]+=-c;
    21         }
    22         
    23     }
    24     long long int sum=0;
    25     for(int i=1;i<=n;i++){
    26         sum=sum+a[i];
    27         s[i]=sum;
    28     }
    29     for(int i=1;i<n;i++){
    30         cout<<s[i]<<" ";
    31     }
    32     cout<<s[n]<<endl;
    33 } 

     C-平衡字符串

    一个长度为 n 的字符串 s,其中仅包含 'Q', 'W', 'E', 'R' 四种字符。

    如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。

    现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?

    如果 s 已经平衡则输出0。

    Input

    一行字符表示给定的字符串s

    Output

    一个整数表示答案

    Examples

    Input
    QWER
    
    Output
    0
    
    Input
    QQWE
    
    Output
    1
    
    Input
    QQQW
    
    Output
    2
    
    Input
    QQQQ
    
    Output
    3
    

    Note

    1<=n<=10^5

    n是4的倍数

    字符串中仅包含字符 'Q', 'W', 'E' 和 'R'.

    思路分析:

      本题使用的方法是尺取法。顾名思义,像尺子一样取一段,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的的情况下。

      对于这道题,我们可以使用暴力方法,我们可以先算出每种字符串的个数,然后算出理想状态下每个字符串的个数,然后我们就可以得到那些多出来的字符串(应该被替换的)有多少,是哪几种。然后我们就可以使用暴力枚举的方法在这个字符串里面寻找这个区间(慢慢的缩小区间长度和逐步推进区间)这个方法的时间复杂度是不可想象的,即便是用二分答案的做法来做,时间复杂度也是我们不能接受的。因此我们采用尺取法。慢慢的扩大我们的尺子的范围,在这个范围内量取我们的可能的答案,然后根据情况来改变我们的区间,这种方法是可取的。

     1 #include<iostream>
     2 #include<string>
     3 #include<cstring>
     4 #include<algorithm>
     5 
     6 using namespace std;
     7 
     8 string s;
     9 int a,b,c,d;
    10 
    11 void fuc(int i){
    12         if(s[i]=='Q'){
    13             a++;
    14         }else if(s[i]=='W'){
    15             b++;
    16         }else if(s[i]=='E'){
    17             c++;
    18         }else{
    19             d++;
    20         }
    21 }
    22 void fuc2(int i){
    23         if(s[i]=='Q'){
    24             a--;
    25         }else if(s[i]=='W'){
    26             b--;
    27         }else if(s[i]=='E'){
    28             c--;
    29         }else{
    30             d--;
    31         }
    32 }
    33 
    34 int main(){
    35     cin>>s;
    36     int sum1,sum2,sum3,sum4;
    37     sum1=sum2=sum3=sum4=0;
    38     for(int i=0;i<s.length();i++){
    39         if(s[i]=='Q'){
    40             sum1++;
    41         }else if(s[i]=='W'){
    42             sum2++;
    43         }else if(s[i]=='E'){
    44             sum3++;
    45         }else{
    46             sum4++;
    47         }
    48     }
    49 //    cout<<sum1<<" "<<sum2<<" "<<sum3<<" "<<sum4<<endl;
    50     int l,r;
    51     l=r=0;
    52     a=b=c=d=0;
    53     int len=s.length();
    54     fuc(0);
    55     while(r<s.length()&&l<=r){
    56         
    57     
    58 //    cout<<l<<" "<<r<<endl<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
    59     int temp=max(sum1-a,sum2-b);
    60     int temp2=max(sum3-c,sum4-d);
    61     int m=max(temp,temp2);
    62     int total=r-l+1;
    63     int free=total-(m-sum1+a)-(m-sum2+b)-(m-sum3+c)-(m-sum4+d);
    64     if(free>=0&&free%4==0){
    65         len=min(len,total);
    66         if(l==r){
    67             l++;
    68             r++;
    69             fuc(l);
    70             fuc2(l-1);
    71         }else{
    72             fuc2(l);
    73             l++;
    74             
    75         }    
    76     }else{
    77         r++;
    78         fuc(r);
    79     }
    80 //    cout<<total<<" len:"<<len<<endl;
    81     }
    82     cout<<len<<endl;
    83     
    84 } 

    D-滑动窗口

    ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如: 
    数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
    Window positionMinimum valueMaximum value
    [1  3  -1] -3  5  3  6  7  -1 3
     1 [3  -1  -3] 5  3  6  7  -3 3
     1  3 [-1  -3  5] 3  6  7  -3 5
     1  3  -1 [-3  5  3] 6  7  -3 5
     1  3  -1  -3 [5  3  6] 7  3 6
     1  3  -1  -3  5 [3  6  7] 3 7

    Input

    输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。 

    Output

    输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。 

    Sample Input

    8 3
    1 3 -1 -3 5 3 6 7
    

    Sample Output

    -1 -3 -3 -3 3 3
    3 3 5 5 6 7

    思路分析:
      本题与单调栈的那题有点相像,使用暴力算法显然是不可取的,我们可以维护一个单调队列来解决这个问题。顾名思义,单调队列就是单调+队列,这里我使用了deq【】数组和tail和head实现了一个简单的单调的队列。对于i来说,要求的是区间[i-k+1,i]的最大值与最小值,建立一个单调递增的队列,依次压入各个数字,若要压入的数不满足队列的单调性,则从队尾元素开始移除,直到要压入的元素压入后不影响单调性。压入之后,队首元素不属于当前范围,应将其出队。此时,队首元素即为最小元素,如此循环,便可求出所有窗口的最小值。源代码如下:


     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 long long p[1000000];
     6 int deq[1000000];
     7 
     8 void fuc1(int n,int k){
     9     int head=0;
    10     int tail=0;
    11 //    deq[tail]=0;
    12 //    tail++;
    13     for(int i=0;i<k;i++){
    14         while(tail!=head&&p[i]<p[deq[tail-1]]){
    15             tail--;
    16         }
    17         deq[tail]=i;
    18         tail++;
    19         while(deq[tail-1]-deq[head]+1>k){
    20             head++;
    21         }
    22     }
    23 //    cout<<tail-head;
    24     cout<<p[deq[head]];
    25     for(int i=k;i<n;i++){
    26         while(head!=tail&&p[i]<p[deq[tail-1]]){
    27             tail--;
    28         }
    29         deq[tail]=i;
    30         tail++;
    31         while(deq[tail-1]-deq[head]+1>k){
    32             head++;
    33         }
    34 //        cout<<tail-head;
    35         cout<<" "<<p[deq[head]];
    36     }
    37 }
    38 
    39 void fuc2(int n,int k){
    40     int head=0;
    41     int tail=0;
    42 //    deq[tail]=0;
    43 //    tail++;
    44     for(int i=0;i<k;i++){
    45         while(tail!=head&&p[i]>p[deq[tail-1]]){
    46             tail--;
    47         }
    48         deq[tail]=i;
    49         tail++;
    50         while(deq[tail-1]-deq[head]+1>k){
    51             head++;
    52         }
    53     }
    54 //    cout<<tail-head+1;
    55     cout<<p[deq[head]];
    56     for(int i=k;i<n;i++){
    57         while(head!=tail&&p[i]>p[deq[tail-1]]){
    58             tail--;
    59         }
    60         deq[tail]=i;
    61         tail++;
    62         while(deq[tail-1]-deq[head]+1>k){
    63             head++;
    64         }
    65     //    cout<<tail-head+1;
    66         cout<<" "<<p[deq[head]];
    67     }
    68 }
    69 
    70 int main(){
    71     int n,k;
    72     cin>>n>>k;
    73     for(int i=0;i<n;i++){
    74         scanf("%lld",&p[i]);
    75     }
    76     fuc1(n,k);
    77     cout<<endl;
    78     fuc2(n,k);
    79 }


  • 相关阅读:
    数学思想方法-python计算战(8)-机器视觉-二值化
    04-05组合问题_算法训练
    至HDFS附加内容
    HDU 1010 Tempter of the Bone heuristic 修剪
    二叉树3种遍历的非递归算法
    [Ramda] R.project -- Select a Subset of Properties from a Collection of Objects in Ramda
    [Ramda] Refactor a Promise Chain to Function Composition using Ramda
    [SVG] Combine Multiple SVGs into an SVG Sprite
    [Ramda] Difference between R.converge and R.useWith
    [Ramda] Refactor to a Point Free Function with Ramda's useWith Function
  • 原文地址:https://www.cnblogs.com/Xu-SDU/p/12568579.html
Copyright © 2011-2022 走看看