第八周程序设计课解题报告

来源:互联网 发布:淘宝手机端免费视频 编辑:程序博客网 时间:2024/05/22 14:36
第八周程序设计课解题报告
    下周期末考试啦,所以提前发布解题报告,留点时间给大家复习吧~
    在写报告前:这周的题略微有点boss的感觉了,因为不再是单纯的暴力可行了,需要你去想如何优化才行了呢,其实这个也是日后程序设计的核心问题之一。很多问题直接暴力写的话很容易,但是往往会消耗(非常多)^n的时间或者空间。那么我们就要去找到题目潜在的规律去优化他,减少不必要的花销。如果不愿意开动脑子的话,当上ceo,迎娶白富美(高富帅)?哼哼,还是安心当一个只会打字的“码农”吧。
    
那么进入正题~
1000. Easy Program
题目大意:给出n个数,然后有m个询问,每次问原来的n个数当中第d个数的值是多少。
解:这个题目其实就是对数组的裸应用,真的不难。不过貌似有些同学是理解错了题意才跪的。我们读入询问d后直接输出a[d]即可(当然我的存储方式是[1,n]而非[0,n-1],使用后者的同学注意下标减一) 
程序:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;

#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

int main(){
    int n, m, a[1111];
    cin>>n>>m;
    for (int i=1; i<=n; i++) cin>>a[i];
    for (int i=1; i<=m; i++){
        int tmp;
        cin>>tmp;
        cout << a[tmp] << endl;
    }
    return 0;
}                        

1001. Reversal
题目大意:首先给出n个数,然后给出m个操作,每次操作给出区间[x,y],假设原来n个数是放置在[1,n]上的,那么每次都把[x,y]这个区间进行翻转操作。最后再输出经过m次操作以后[1,n]上的数的数值。
解:不断进行交换操作就好了,但是我发现很多同学喜欢把交换的区间映射到[1,y-x+1]上,然后再进行翻转,其实这样不是不行,时间也不会慢,但是会使得你的下标比较容易写错。其实交换步骤变成x与y交换,然后x--,y++,直到x>y就好了。
哦对了,还有输出格式的问题,注意最后一个数后面是【没有空格】的,同时记得【换行】! 


程序:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;

#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

int main(){
    int n, m, a[1111];
    cin>>n>>m;
    for (int i=1; i<=n; i++) cin>>a[i];
    for (int i=1; i<=m; i++){
        int x, y, kok;
        cin>>x>>y;
        while (x<=y){
            kok=a[x]; a[x]=a[y]; a[y]=kok;
            x++; y--;
        }
    }
    for (int i=1; i<=n; i++){
        cout << a[i];
        if (i<n) cout <<' ';
        else cout << endl;
    }
    return 0;
}                                  

1002. The Highest Guy
题目大意:给出n个数,再给m个询问,每次询问第b个人到第n个人中最高的人的身高(区间[b,n]上的最大数)
解:哼哼哼,这题很简单啊输入,然后每次找区间最大的就好了。我交,卧槽,怎么tle了?
还记得我之前所讲的tle如何查错么,算一算如果出数据的人满怀恶意的话这题会怎么出数据卡你呢?自然是让你每次的询问都花费时间最大就好了。每次都询问1到n的最大值。而暴力寻找区间最大值的循环次数是区间长度(就是n),那么加上m次询问的话【时间复杂度】就是O(n*m),题目数据n,m,都去到10w,也就是说,最坏情况下,这题的复杂度达到100亿,然而我们的计算机一秒大概跑1亿次,而时间限制只有1秒。所以肯定会超时咯。

怎么优化呢?m次询问肯定少不了了,那么每次询问是否一定要n次呢?有没有非常神的办法呢?对于1个数,那么我们肯定1眼看出来了吧。那么对于多个数,能否做到1眼看出来呢?

注意题目有一个特殊性质,询问的是第b个人到n个人的最大值。那么对于b-1呢?是不是会有重复的东西? 

当询问为b==n时,我们一眼望出来了,对于b==n-1时,是不是可以看两眼?对于b==n-2,望3眼?等等!我在看b==n-1时,如果把[n-1, n]的最大值保存下来放到help[n-1]里,是不是不用看[n-1,n]的那两眼了。好的,我们看了a[n-2],help[n-1]后,把最大值放入help[n-2]里,在观察[n-3,n]的时候,是不是只需要观察a[n-3]和help[n-2]就好了?如此类推,我们要完成整个help数组的记录,只需要2*n-1次即可了?实际上help[i]的意义就是记录了从第 i个数到第n个数的最大值。所以我们读入n后进行预处理得出help数组,询问的时候直接用上就好了,每次询问的处理显然是O(1)的。

那么原程序的时间复杂度已经下降到O(n+m)了(为啥少了东西?因为时间复杂度忽略常数)。这显然可以满足题目的要求了
对了,输入输出的次数多的时候记得不要用cin和cout,这两个东西非常非常非常非常非常慢!
程序:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;

#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

int a[111111], maxx[111111];

int main(){
    int n, m;
    cin>>n>>m;
    for (int i=1; i<=n; i++) scanf("%d", &a[i]);
    maxx[n]=a[n];
    for (int i=n-1; i; i--){
        maxx[i]=MAX(a[i], maxx[i+1]);

    }
    int tmp;
    for (int i=1; i<=m; i++){
        scanf("%d", &tmp);
        printf("%d\n", maxx[tmp]);
    }
    return 0;
}               

1003. Yaroslav and Permutations                  
题目大意:给出n个数,问是否能通过若干次交换,反正就是能够把他变成相邻两个数不会相等即可。
解:很容易猜想到, 这个问题的关键是n个数中重复次数最多的数不要超过(n+1)/2个即可存在一种办法把数字都隔开。所以问题的关键在于找出最多的数有多少个。排序后统计?其实不必,因为观察到题目中有性质n个数的数值都在1~1000内,所以我们开一个数组f,f[i]表示i这个数字出现了几次即可。(这个办法其实也叫基数排序)详情看程序可能好理解一点。

程序:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;

#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

int a[111111], maxx[111111], f[1111];

int main(){
    int n;
    scanf("%d", &n);
    memset(f, 0, sizeof(f));
    for (int i=1; i<=n; i++){
        int tmp;
        scanf("%d", &tmp);
        f[tmp]++;
    }
    bool flag=true;
    for (int i=1; i<=1000; i++) if (f[i]>(n+1)/2){
        flag=false; break;
    }
    if (flag) cout << "YES" << endl;
    else cout << "NO" << endl;
    return 0;
}         

 
1004. Sum                                                    
题目大意:给出n个数,每次询问[x,y]这个区间内的所有数的和是多少。
解:这题为啥会超时的分析可以参见第三题。我们考虑如何优化。这题是否能够利用已有的信息使得询问的时候不用重复观察呢?我们先考虑如果询问特殊化把,每次都会询问[1,y],那么自然可以用一个数组预处理出[1,y]的和。但是题目要求的是[x,y]怎么办?其实,询问的区间是连续的,那么[x,y]是否等价于[1,y]-[1,x-1]呢?

程序:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;

#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

int a[111111], maxx[111111], f[1111];

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; i++){
        scanf("%d", &a[i]);
        a[i]+=a[i-1];
    }
    a[0]=0;
    int x, y;
    for (int i=1; i<=m; i++){
        scanf("%d%d", &x, &y);
        printf("%d\n", a[y]-a[x-1]);
    }

    return 0;
}                               

最后想说两段话:
1.不知道有多少同学有去听周三晚林翰老师的信导课呢?在关于解题思路这一段,其实本周作业就有契合的地方。一是不必要详细证明自己的猜想 ,这便是第四题,我们得到一个用常识判断是对的策略,然而要数学证明就难了,有时候不必拘泥于这种问题。二是可以从小的数据出发去推出全局策略,我在第三题的优化步骤其实就是从小的出发,把问题特殊化,从区间为1,2,3(长度)慢慢推出来,再总结能用于一般化的策略,最后就完成了优化了。
2.时候看自己写的报告其实还是有不少错误的,如果有错,欢迎各种方式指正,如果有疑问或者想进一步了解的知识点,欢迎以各种方式找我。能帮助到大家,才是我写这份解题报告的最大快乐:)
0 0
原创粉丝点击