• 2019/1/5考试题解


    T1整数划分

    题目描述

    读入一个正整数n。要求将n写成若干个正整数之和,并且使这些正整数的乘积最大。例如,n=13,则当n表示为4+3+3+3(或2+2+3+3+3)时,乘积=108为最大。

    输入

    一个整数,n。

    输出

    第1行输出一个整数,为最大乘积的位数。第2行输出最大乘积的前100位,如果不足100位,则按实际位数输出最大乘积。(提示:在给定的范围内,最大乘积的位数不超过5000位)。

    样例输入

    13

    样例输出

    3
    108

    数据范围

    10 ≤ n ≤ 31000

    解题思路

    首先确定一点,这道题是需要用高精的,=-=估计看得一些人头疼,毕竟高精这种东西有些难弄。一个还好,如果要写多个高精那简直令人崩溃。

    我们首先就要确定方法。其实看着这道题我们可以想到一个非常简单的DP(没想到吧,这种题还能用DP)

    dp[i] = max(dp[j] * dp[i -j])

    够简单吧。。。但这里需要弄一下初值,也就是dp[2] = 2 , dp[3] = 3。为什么要这样弄呢?我们发现如果用其他的数凑成2,dp[2]的最大值只能是1,但这样还不如用它本身来的划算。

    这一个DP有两个变量,以这样的数据范围,枚举直接爆了。考试的时候,我就用的这一个方法,我们就必须找更多的性质,于是,打一下表

    i = 1 dp[i] = 1
    i = 2 dp[i] = 2 (1)
    i = 3 dp[i] = 3 (2)
    i = 4 dp[i] = 4
    i = 5 dp[i] = 6
    i = 6 dp[i] = 9
    i = 7

    dp[i] = 12

    i = 8 dp[i] = 18
    i = 9 dp[i] = 27
    i = 10 dp[i] = 54

    看起来并没有什么规律。。。但我们看看,除了2,3,其余的dp值都比i的值大,然而看看其他的dp值,诶。。。好像正是2与3组合所成。。的确,那么我们仅仅需要将n分成由2与3组成的部分就可求出最优了。后面的所有dp值,本质上都是2与3所凑。从5开始,所有的dp值都比i大了。

    那肯定2与3也要优先选一个先累计啊,我们又需要进行讨论,我们找比较特殊的点6.

    可以知道6可以分成(2,2,2)或者(3,3)。很明显,后者更优。那么我们就可以知道,一个数如果分出来2的个数大于等于3,我们是可以将它们3个一对转换成2个3的。那么2的个数我们就控制在了2个以内。所以,这道题就是优先分3

    一个特殊点:到了4,肯定分2个2,不是3与1。

    那么这道题也仅仅需要高精乘,代码并不难。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    int n , a[100005] , c[5005];
    inline void cheng(int x){
        register int xx = 0 , i;
        for (i = 1;i <= c[0]  || xx;i ++){
            c[i] = c[i] * x + xx;
            xx = c[i] / 10;
            c[i] %= 10;
        }
        c[0] = i;
        while (c[c[0]] == 0 && c[0] > 1)
            c[0] --;
    }
    int main(){
        scanf("%d",&n);
        c[0] = c[1] = 1;
        register int tot = n / 3 ;
        register int tot1 = n % 3 , i;
        if (tot1 == 1){
            tot --;
            tot1 += 3;
        }
        for (i = 1;i <= tot ;i ++)
            cheng(3);
        if (tot1)
            cheng(tot1);    
        printf("%d
    ",c[0]);
        for (i = c[0] ;i >= max (c[0] - 99 ,1) ; i --)
            printf("%d",c[i]);
    }

    总结

    事实证明思路还是多去实现,这样就一步一步可能就可以找到更好的方法,打出正解。对于基本性质,要能看出来对于我来说还是有些困难。

    T2地震

    题目描述

    农夫John的农场遭受了一场地震。有一些牛棚遭到了损坏,但幸运地,所有牛棚
    间的路经都还能使用。FJ的农场有P个牛棚,编号1..P,  C条双向路经连接这些牛
    棚,编号为1. . C。路经i连接牛棚ai和bi,路经可能连接ai到它自己,两个牛棚之
    间可能有多条路经。农庄在编号为1的牛棚.,N头在不同牛棚的牛通过手机短信
    reroortj告诉FJ它们的牛棚(reportj)没有损坏,但是它们无法通过路经和没有损坏
    的牛棚回到到农场。当FJ接到所有短信之后,找出最小的不可能回到农庄的牛榭
    数目。这个数目包括损坏的牛棚。

    输入

    第1行:三个空格分开的数:P, C,和N
    第2 ...C+1行:每行两个空格分开的数:ai和bi
    第C+2 ...C+N+1行:每行一个数:reportj

    输出

    第1行:一个数,最少不能回到农庄的牛的数目(包括损坏的牛棚)

    样例输入

    4 3 1
    1 2
    2 3
    3 4
    3

    样例输出

    3

    解题思路

    有些人可能不理解题意,其实可以这样理解,reportj就表示它不能够回到1,那么为啥它不能回到1呢?那么肯定它能够到1的某个必经之点是有问题的(要么破损,要么不能够回到1),由于我们要求的是最小值,我们尽量要让这个有问题的必经之点最优。

    我们肯定令这一个点周围与它直接相连的点有问题即可。其实也很好看出来,如果说,你走了很远(中途经历了很多的点),这个时候你才令下一个必走的点断掉,那么前面走过的那些点都到不了1了。因为如果前面走过的点少了断掉的这一点还能到1,那么不能到1的那个点依旧可以到1,不合题意还要删除点。那岂不是很惨。违背了最优性,因此我们控制一下,尽量将范围锁定在打电话的这一个点的周围。于是,仅仅将与它直接相连的点删除标记即可。

    本校大佬的图例(其实是题解,表示我不想画图)

    暴力出奇迹,标记了点,dfs从1开始看哪些点能遍历到吧。最后统计就行了。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<iostream>
    #include<vector>
    #include<algorithm>
    #define P 30005
    using namespace std;
    int p , c , n , ans;
    int mov[P];
    vector<int>G[P];
    void dfs(int x){
        int xx = G[x].size();
        for (int i = 0; i < xx ; i ++){
            if (mov[G[x][i]] == 0){
                mov[G[x][i]] = 1;
                dfs(G[x][i]);
            }
        }
    }
    int main(){
        scanf ("%d%d%d",&p,&c,&n);
        for (int i = 1 ;i <= c ;i ++){
            int a,b ;
            scanf ("%d%d",&a,&b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        for (int i = 1 ;i <= n ;i ++){
            int j ;
            scanf ("%d",&j);
            mov[j] = -1;
            for (int k = 0 ;k <G[j].size(); k ++)
                mov[G[j][k]] = -1;
        }
        mov[1] = 1;
        dfs(1);
        for (int i = 2;i <= p ;i ++)
            if (mov[i] == -1 || mov[i] == 0)
                ans ++ ;
        printf("%d",ans);
    }

    总结

    考试的时候成功没读懂题目,导致草草暴力过了样例。事后看看发现题意描述还是比较清晰的,只是因为我可能没有遇到过这种类型的题,因此思路无法转换,自然就做不出了。事实证明还是要多多做题,找到自己的问题。见识不同的题型,这样就可以使自己做题的时候可以不为题目而弄这么久了。

    T3最长上升子序列

    题目描述

    给出一个长度为N的整数序列,求出包含它的第K个元素的最长上升子序列。

    输入

    第一行两个整数N, K
    第二行N个整数

    输出

    如题目所说的序列长度。

    样例输入

    8 6
    65 158 170 299 300 155 207 389

    样例输出

    4

    数据范围

    0 < N ≤ 200000,0 < K ≤ N

    解题思路

    这道题可以说解决的方式还是比较多的,最长上升子序列作为基础的DP想必各位都会。

    朴素的DP算法是n^{2}的时间复杂度。做这道题,必须掌握进阶的算法。

    的确,最长上升子序列是可以利用二分在O(n*log(n))的时间复杂度下完成。

    欲知如何做,可以看看一道求最长不下降序列的题

    知道了优化方法,这道题可以说是轻松很多了,很多方式都可以解决,这里列举一下

    1. 1到k求一个最长上升。n到k求一个最长下降,当放入k的时候,就得到了序列的长度。最后相加-1即可。
    2. 去掉1到k中小于a[k]的数,然后1到n求最长上升子序列。
    3. 从1到k-1中求一个最长上升,比a[k]大的数就不处理,从k + 1到n求一个最长上升,同样,比a[k]小的就不管。

    主要的思路方法还是差不多。考的算法也很单一。

    这里的二分可以用lower_bound来运算,但我不经常使用,这个函数是比较方便的。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<iostream>
    #include<vector>
    #include<algorithm>
    #define N 200005
    using namespace std;
    int n , k , a[N] , dp[N] , len , ans;
    int main(){
        scanf ("%d%d",&n,&k);
        for (int i = 1 ;i <= n;i ++){
            scanf("%d",&a[i]);
        } 
    	for (int i = 1 ;i <= n ;i ++){
    		if ((i < k && a[i] >= a[k]) || (i > k && a[i] <= a[k]))
    			continue;
    		if (a[i] > dp[len])
    			dp[++ len] = a[i];
    		else{	
    			int j =lower_bound(dp + 1,dp + 1 + len,a[i]) - dp;
    			dp[j] = a[i];
    			len = max(len,j);
    		}
    	}
        printf("%d",len);
    }

    总结

    考试的时候莫名错了,感到很奇怪,然而事后一打,一遍过,这就让我感到疑惑了,我考试的时候到底是哪里写得有问题呢?反正问题是有的,看起来又是很简单的细节问题,以后还是要注意一下,这种失分真的得不偿失啊。

    考试总结

    没有太大的问题,关键还是细节,这可以说是十分重要的。时间我觉得还是比较充裕的,打完代码后还是有一段不少的时间,然而代码的质量其实不算太高。发现考试打的代码都好复杂,事后再来做发现轻松多了。这便也说明了考试还真是考察综合能力,不仅仅只有编程,所以啊,只有经过多次的考试,才可能够在考场真正发挥自我的能力。

  • 相关阅读:
    对象直接量
    js学习类
    jquery.js与sea.js综合使用
    拥抱模块化的JavaScript
    匿名函数与闭包
    js对象如何合并?
    Web.config配置文件详解
    javascipt自定义命名空间、静态类、实例对象
    jQuery源码的基础知识
    企业架构/企业开发 [Enterprise architecture / Enterprise Development]
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566693.html
Copyright © 2020-2023  润新知