第九届蓝桥杯(软件类)省赛C++A组真题题解
题目链接
题目结构
| 题目 | 类型 | 分值 | 
|---|---|---|
| 第一题 | 结果填空 | 5分 | 
| 第二题 | 结果填空 | 7分 | 
| 第三题 | 结果填空 | 9分 | 
| 第四题 | 结果填空 | 13分 | 
| 第五题 | 代码填空 | 11分 | 
| 第六题 | 程序设计 | 17分 | 
| 第七题 | 程序设计 | 19分 | 
| 第八题 | 程序设计 | 21分 | 
| 第九题 | 程序设计 | 23分 | 
| 第十题 | 程序设计 | 25分 | 
第一题 分数
-  问题重现 1/1 + 1/2 + 1/4 + 1/8 + 1/16 + … 
 每项是前一项的一半,如果一共有20项,求这个和是多少,结果用分数表示出来。
 类似:3/2当然,这只是加了前2项而已。分子分母要求互质。输出 按格式输出答案 
-  解题思路 这是一个等比数列,首项为 1 1 1,公比为 1 / 2 1/2 1/2,那么根据等比数列求和公式我们可得 s = a 1 ( 1 − q n ) 1 − q s=\frac{a_1(1-q^n)}{1-q} s=1−qa1(1−qn),求出分子分母,再利用欧几里得算法约分即可得到答案。 
-  代码 
/**
  *@filename:分数
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-03 09:46
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
ll gcd(ll n,ll m){
    return n%m?gcd(m,n%m):m;
}
//我们发现,这实际上就是一个等比数列,公比为1/2.
void solve(){
    //利用等比数列求和公式a1(1-q^n)/(1-q)。当然我们也可以利用gcd来通分求和再约分。
    ll a=ll(powl(2,20)-1),b=ll(powl(2,19));
    cout<<a/gcd(a,b)<<"/"<<b/gcd(a,b)<<endl;//1048575/524288
}
int main(){
    solve();
    return 0;
}
-  答案 1048575 / 524288 1048575/524288 1048575/524288 
第二题 星期一
-  问题重现 整个 20 20 20世纪( 1901 1901 1901年 1 1 1月 1 1 1日至 2000 2000 2000年 12 12 12月 31 31 31日之间),一共有多少个星期一? 
 (不要告诉我你不知道今天是星期几)输出 输出一个整数表示答案 
-  解题思路 先求出从 1901 1901 1901年 1 1 1月 1 1 1日至 2000 2000 2000年 12 12 12月 31 31 31日之间有多少天,然后我们需要确定 2000 2000 2000年 12 12 12月 31 31 31日是星期几,这我们可以通过日历来查看,也可以直接根据当前日期推回去,知道了之后,我们就可以通过对 7 7 7整除和求余来判断共有几天了。 
-  代码 
/**
  *@filename:星期一
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-03 09:54
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
//计算这期间有多少天然后根据日历判断初始时星期几即可。
//我们知道平年是365,闰年是366.
bool check(int year){
    if((year%4==0&&year%100!=0)||year%400==0){
        return true;
    }
    return false;
}
void solve(){
    int ans=0;
    for(int i=1901;i<=2000;i++){
        if(check(i)){
            ans+=366;
        }
        else{
            ans+=365;
        }
    }
    //利用日历,也可以根据现在的时间推回去。这里采用直接看日历的方式:2000年12月31日为星期一
    //如果根据现在的时间推回去就是计算现在里2000年12月31日有多少天再做处理。
    cout<<ans/7<<endl;//5217
    cout<<ans%7<<endl; //由于余6,构不成。
}
int main(){
    solve();
    return 0;
}
-  答案 5217 5217 5217 
第三题 乘积尾零
-  问题重现 如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零? 5650 4542 3554 473 946 4114 3871 9073 90 4329 2758 7949 6113 5659 5245 7432 3051 4434 6704 3594 9937 1173 6866 3397 4759 7557 3070 2287 1453 9899 1486 5722 3135 1170 4014 5510 5120 729 2880 9019 2049 698 4582 4346 4427 646 9742 7340 1230 7683 5693 7015 6887 7381 4172 4341 2909 2027 7355 5649 6701 6645 1671 5978 2704 9926 295 3125 3878 6785 2066 4247 4800 1578 6652 4616 1113 6205 3264 2915 3966 5291 2904 1285 2193 1428 2265 8730 9436 7074 689 5510 8243 6114 337 4096 8199 7313 3685 211输出 输出一个整数表示答案 
-  解题思路 我们要知道,能构成相乘为 0 0 0的最小因子对是 2 2 2与 5 5 5 ,所以我们实际上就是看这些数有多少个 2 2 2因子和 5 5 5因子。取最小的数量即是答案。 
-  代码 
/**
  *@filename:乘积尾零
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-03 10:30
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;  
//这道题实际上就是将数分解,然后对于分解之后的因数相乘之后只有2和5相乘才能凑0,所以我们可以统计2和5因子的数量即可。
int main(){
    int temp;
    int cnt2=0,cnt5=0;
    while(cin>>temp&&temp!=1){//这里采用1作为结束标志。
        while(temp%2==0||temp%5==0){
            if(temp%2==0){
                cnt2++;
                temp/=2;
            }
            if(temp%5==0){
                cnt5++;
                temp/=5;
            }
        }
    }
    cout<<min(cnt2,cnt5)<<endl;//31
    return 0;
}
-  答案 31 31 31 
第四题 第几个幸运数
-  问题重现 到x星球旅行的游客都被发给一个整数,作为游客编号。 
 x星的国王有个怪癖,他只喜欢数字3,5和7。
 国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品。
 前10个幸运数字是:3 5 7 9 15 21 25 27 35 45,因而第11个幸运数字是:49
 小明领到了一个幸运数字 59084709587505。
 去领奖的时候,人家要求他准确说出这是第几个幸运数字,否则领不到奖品。
 请你帮小明计算一下,59084709587505是第几个幸运数字。输出 输出一个整数表示答案 
-  解题思路 看到这个问题我们应该都会想到利用 s e t set set容器吧,因为它会自动排序 + + +去重,所以我们只要用最初始的元素 3 , 5 , 7 3,5,7 3,5,7不停的去更新。这里需要注意的就是我们选择更新点的时候是选取比上一个更新点第一个大于它的,这可以利用 u p p e r _ b o u n d upper\_bound upper_bound函数来实现,这样才能确保每一个元素都被求到了。最后输出容器的大小即可。 
-  代码 
/**
  *@filename:第几个幸运数字
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-03 23:57
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
set<ll> t;
ll maxx=59084709587505;
ll a[]={3,5,7};
void solve(){
    ll now,head=1;
    while(true){
        for(int i=0;i<3;i++){
            now=head*a[i];
            if(now<=maxx){
                t.insert(now);
            }
        }
        //更新起点。
        head=*t.upper_bound(head);
        if(head==maxx){
            break;
        }
    }
    cout<<head<<endl;
    cout<<t.size()<<endl;//1905
}
int main(){
    solve();
    return 0;
}
-  答案 1905 1905 1905 
第五题 打印图形
-  问题重现 如下的程序会在控制台绘制分形图(就是整体与局部自相似的图形)。 当n=1,2的时候,输出如下: 请仔细分析程序,并填写划线部分缺少的代码。  源程序: #include <stdio.h> #include <stdlib.h> void show(char* buf, int w){ int i,j; for(i=0; i<w; i++){ for(j=0; j<w; j++){ printf("%c", buf[i*w+j]==0? ' ' : 'o'); } printf("\n"); } } void draw(char* buf, int w, int x, int y, int size){ if(size==1){ buf[y*w+x] = 1; return; } int n = _________________________ ; //填空 draw(buf, w, x, y, n); draw(buf, w, x-n, y ,n); draw(buf, w, x+n, y ,n); draw(buf, w, x, y-n ,n); draw(buf, w, x, y+n ,n); } int main() { int N = 3; int t = 1; int i; for(i=0; i<N; i++) t *= 3; char* buf = (char*)malloc(t*t); for(i=0; i<t*t; i++) buf[i] = 0; draw(buf, t, t/2, t/2, t); show(buf, t); free(buf); return 0; }
-  解题思路 题目中是利用一维数组来实现二维画布的。我们看图形会发现,其实 n − 1 n-1 n−1的图形就是 n n n的图形的上下左右的子图。所以,程序是利用递归来实现的,又因为 n n n每 + 1 +1 +1,图形的边长就扩大 3 3 3倍,程序中的 d r a w draw draw函数参数也很明显, x , y x,y x,y代表中央坐标, s i z e size size表示当前图形的边长,而之后递归求解的就是中左右上下子图,所以传递的 n n n就是子图的边长大小,故 n = s i z e / 3 n=size/3 n=size/3。 
-  答案 size/3
第六题 航班时间
-  问题重现 小h前往美国参加了蓝桥杯国际赛。小h的女朋友发现小h上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。 
 小h对超音速飞行感到十分恐惧。仔细观察后发现飞机的起降时间都是当地时间。由于北京和美国东部有12小时时差,故飞机总共需要14小时的飞行时间。
 不久后小h的女朋友去中东交换。小h并不知道中东与北京的时差。但是小h得到了女朋友来回航班的起降时间。小h想知道女朋友的航班飞行时间是多少。
 对于一个可能跨时区的航班,给定来回程的起降时间。假设飞机来回飞行时间相同,求飞机的飞行时间。输入 一个输入包含多组数据。 
 输入第一行为一个正整数T,表示输入数据组数。
 每组数据包含两行,第一行为去程的 起降 时间,第二行为回程的 起降 时间。
 起降时间的格式如下h1:m1:s1 h2:m2:s2 h1:m1:s1 h3:m3:s3 (+1) h1:m1:s1 h4:m4:s4 (+2) 表示该航班在当地时间h1时m1分s1秒起飞, 
 第一种格式表示在当地时间 当日 h2时m2分s2秒降落
 第二种格式表示在当地时间 次日 h3时m3分s3秒降落。
 第三种格式表示在当地时间 第三天 h4时m4分s4秒降落。
 对于此题目中的所有以 hⓂ️s 形式给出的时间, 保证 ( 0<=h<=23, 0<=m,s<=59 ).
 保证输入时间合法,飞行时间不超过24小时。输出 对于每一组数据输出一行一个时间hh:mm:ss,表示飞行时间为hh小时mm分ss秒。 
 注意,当时间为一位数时,要补齐前导零。如三小时四分五秒应写为03:04:05。样例输入 3 17:48:19 21:57:24 11:05:18 15:14:23 17:21:07 00:31:46 (+1) 23:02:41 16:13:20 (+1) 10:19:19 20:41:24 22:19:04 16:41:09 (+1)样例输出 04:09:05 12:10:39 14:22:05
-  解题思路 我们这道题需要理解时差与起飞和降落时间的关系,设 A A A地到 B B B地的时差为 t t t,从 A A A地出发时间为 t 1 t_1 t1, B B B地降落时间为 t 2 t_2 t2。从 B B B地出发时间为 t 3 t_3 t3, A A A地降落时间为 t 4 t_4 t4,飞机的飞行时间为 t 5 t_5 t5。 则有: t 2 − t 1 + t = t 5 ( 1 ) t 4 − t 3 − t = t 5 ( 2 ) t2-t_1+t=t_5\space\space\space\space\space\space (1)\space\space\space\space\space\space t4-t_3-t=t_5\space\space\space\space\space\space (2) t2−t1+t=t5 (1) t4−t3−t=t5 (2)。 联立 ( 1 ) ( 2 ) (1)(2) (1)(2)式即可消去未知数时差 t t t,那么 t 5 = ( t 4 − t 3 + t 2 − t 1 ) / 2 t_5=(t_4-t_3+t_2-t_1)/2 t5=(t4−t3+t2−t1)/2。 知道了这个,我们就可以去写题了,对于这种题,我们可以有效的利用 s c a n f scanf scanf函数,将其中的影响字符处理。这样就可以求解了。 
-  代码 
/**
  *@filename:航班时间
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-04 00:35
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
struct Date{
    int h,m,s;
};
Date st,ed;//出发日期和截止日期。
int getTime(){
    scanf("%d:%d:%d %d:%d:%d",&st.h,&st.m,&st.s,&ed.h,&ed.m,&ed.s);
    //判断接下来是否是(
    int temp=0;
    if(getchar()!='\n'){
        scanf("(+%d)",&temp);
    }
    //计算时间差。
    ed.h-=st.h,ed.m-=st.m,ed.s-=st.s;
    return temp*24*3600+ed.h*3600+ed.m*60+ed.s;
}
void solve(){
}
int main(){
    int t;
    //通过两个地方的飞行时间相加取平均数抵消时差,得到飞行时间。
    while(cin>>t){
        while(t--){
           int t1=getTime();
           int t2=getTime();
           int result=(t1+t2)/2;
           printf("%02d:%02d:%02d\n",result/3600,result/60%60,result%60);
        }
    }  
    solve();
    return 0;
}
第七题 三体攻击
-  问题重现 三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A × B × C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。其中,第 i 层第 j 行第 k 列的战舰(记为战舰 (i, j, k))的生命值为 d(i, j, k)。 
 三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 t 轮攻击用 7 个参数 lat, rat, lbt, rbt, lct, rct, ht 描述;
 所有满足 i ∈ [lat, rat],j ∈ [lbt, rbt],k ∈ [lct, rct] 的战舰 (i, j, k) 会受到 ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
 地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。输入 第一行包括 4 个正整数 A, B, C, m; 
 第二行包含 A × B × C 个整数,其中第 ((i − 1)×B + (j − 1)) × C + (k − 1)+1 个数为 d(i, j, k);
 第 3 到第 m + 2 行中,第 (t − 2) 行包含 7 个正整数 lat, rat, lbt, rbt, lct, rct, ht。
 A × B × C ≤ 10^6, m ≤ 10^6, 0 ≤ d(i, j, k), ht ≤ 10^9。输出 输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。 样例输入 2 2 2 3 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 2样例输出 2提示 在第 2 轮攻击后,战舰 (1,1,1) 总共受到了 2 点伤害,超出其防御力导致爆炸。 
-  解题思路 这道题暴力求解应该很容易想到,模拟攻击遍历战舰即可,这样能过 50 % 50\% 50%的数据。但这我们肯定不满足,所以我们可能会换一种思路,由于攻击次数一定是在 [ 1 , m ] [1,m] [1,m]之间的,所以我们可能会想到二分来枚举答案。可是我们如果枚举了答案之后,更新战舰的生命值呢?是直接一轮一轮遍历嘛,这样的效率更直接遍历是没什么区别的,所以这里会利用到差分数组,且是三维差分,这里只给出代码,之后补思路,(目前只会一维差分数组)。 
-  暴力代码 
/**
  *@filename:三体攻击
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-07 16:43
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
//暴力枚举时间复杂度高达O(abcm),为10^12,肯定会超时,大概只能过30%的数据,水一些分。
int a,b,c,m;//m轮攻击。
int main(){
    while(cin>>a>>b>>c>>m){
        vector<vector<vector<int> > > planes(a+1,vector<vector<int> >(b+1,vector<int>(c+1)));
        vector<vector<int> > hitInfo(m,vector<int>(7));
        for(int i=1;i<=a;i++){
            for(int j=1;j<=b;j++){
                for(int k=1;k<=c;k++){
                    cin>>planes[i][j][k];
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<7;j++){
                cin>>hitInfo[i][j];
            }
        }
        //开始暴力枚举攻击。
        int lat,rat,lbt,rbt,lct,rct,ht;
        for(int t=0;t<m;t++){
            lat=hitInfo[t][0],rat=hitInfo[t][1],lbt=hitInfo[t][2],rbt=hitInfo[t][3],
            lct=hitInfo[t][4],rct=hitInfo[t][5],ht=hitInfo[t][6];
            for(int i=lat;i<=rat;i++){
                for(int j=lbt;j<=rbt;j++){
                    for(int k=lct;k<=lct;k++){
                        planes[i][j][k]-=ht;
                        if(planes[i][j][k]<0){
                            cout<<t+1<<endl;
                            return 0;
                        }
                    }
                }
            }
        }
    }
    return 0;
}
- 二分+三维差分处理代码
#include <iostream>
#include <cstring>
const int MAX_N = 1000010;
const int MAX_M = 1000010;
int A, B, C;
long long _d[MAX_N], _v[MAX_N];
struct Mod {int la, ra, lb, rb, lc, rc, a;} mod[MAX_M];
#define pos(i, j, k) ((i) * B + (j)) * C + (k)
#define d(i, j, k) _d[pos(i, j, k)]
#define v(i, j, k) _v[pos(i, j, k)]
inline void add(int i, int j, int k, int a) {
    if(i < A && j < B && k < C) v(i, j, k) += a;
}
int main() {
    std::ios::sync_with_stdio(0); std::cin.tie(0); std::cout.tie(0);
    int M; std::cin >> A >> B >> C >> M;
    for(int i = 0; i < A; ++i) for(int j = 0; j < B; ++j) 
        for(int k = 0; k < C; ++k) std::cin >> d(i, j, k);
    for(int i = 1; i <= M; ++i) {
        int q, w, e, r, t, y, u;
        std::cin >> q >> w >> e >> r >> t >> y >> u;
        mod[i] = {q - 1, w, e - 1, r, t - 1, y, u};
    }
    int l = 1, r = M, m;
    while(l != r) {
        m = (l + r) >> 1;
        memset(_v, 0, (A * B * C) << 3);
        for(int i = 1; i <= m; ++i) {
            auto& m = mod[i];
            add(m.la, m.lb, m.lc, m.a);
            add(m.ra, m.lb, m.lc, -m.a);
            add(m.la, m.rb, m.lc, -m.a);
            add(m.la, m.lb, m.rc, -m.a);
            add(m.ra, m.rb, m.lc, m.a);
            add(m.ra, m.lb, m.rc, m.a);
            add(m.la, m.rb, m.rc, m.a);
            add(m.ra, m.rb, m.rc, -m.a);
        }
        for(int i = 1; i < A; ++i) for(int j = 0; j < B; ++j) for(int k = 0; k < C; ++k) v(i, j, k) += v(i - 1, j, k);
        for(int i = 0; i < A; ++i) for(int j = 1; j < B; ++j) for(int k = 0; k < C; ++k) v(i, j, k) += v(i, j - 1, k);
        for(int i = 0; i < A; ++i) for(int j = 0; j < B; ++j) for(int k = 1; k < C; ++k) v(i, j, k) += v(i, j, k - 1);
        for(int i = 0, _N = A * B * C; i < _N; ++i) if(_v[i] > _d[i]) goto ok;
        l = m + 1; continue;
        ok: r = m;
    }
    std::cout << l << std::endl;
}
第八题 全球变暖
-  问题重现 你有一张某海域 N × N N\times N N×N像素的照片,".“表示海洋、”#"表示陆地,如下所示: ....... .##.... .##.... ....##. ..####. ...###. .......其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。 
 由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
 具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
 例如上图中的海域未来会变成如下样子:....... ....... ....... ....... ....#.. ....... .......请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。 输入 第一行包含一个整数N。 (1 <= N <= 1000) 
 以下N行N列代表一张海域照片。
 照片保证第1行、第1列、第N行、第N列的像素都是海洋。输出 一个整数表示答案。 样例输入 7 ....... .##.... .##.... ....##. ..####. ...###. .......样例输出 1
-  解题思路 d f s dfs dfs判别连通分量,思想是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点。当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直至所有的顶点都被访问过。 所以这道题我们就是判别有几个岛会被淹没,这就和我们搜索过程中判断每个顶点是否存在海洋有关,此题要注意的就是标记访问过的点,即将一个连通分量上的点全部标记。 
-  代码 
/**
  *@filename:全球变暖
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-07 20:36
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000 + 5;
const int mod = 1e9+7;
int n;
string graph[maxn];
int go[][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool vis[maxn][maxn];//vis[i][j]表示该陆地像素是否已经被访问。
bool flag;//标志变量,判断岛是否被淹。
void dfs(int x,int y){
    //判断连通的像素。
    vis[x][y]=true;
    //cout<<x<<" "<<y<<endl;
    if(graph[x-1][y]=='#'&&graph[x+1][y]=='#'&&graph[x][y-1]=='#'&&graph[x][y+1]=='#'){
        flag=false;//说明该岛不会被淹。
    }
    for(int i=0;i<4;i++){
        int tx=x+go[i][0],ty=y+go[i][1];
        if(graph[tx][ty]=='#'&&!vis[tx][ty]){
            dfs(tx,ty);
        }
    }
}
void solve(){
    int cnt=0;
    memset(vis,false,sizeof(vis));
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(graph[i][j]=='#'&&!vis[i][j]){
                flag=true;
                dfs(i,j);
                if(flag)cnt++;
            }
        }
    }
    cout<<cnt<<endl;
}
int main(){
    while(cin>>n){
        for(int i=0;i<n;i++){
            cin>>graph[i];
        }
        solve();
    }
    return 0;
}
第九题 倍数问题
-  问题重现 众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。 
 但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。
 现在小葱给了你 n 个数,希望你从这 n 个数中找到三个数
 使得这三个数的和是 K 的倍数,且这个和最大。数据保证一定有解。输入 第一行包括 2 个正整数 n, K。 
 第二行 n 个正整数,代表给定的 n 个数。
 1 <= n <= 10^5, 1 <= K <= 10^3,给定的 n 个数均不超过 10^8。输出 输出一行一个整数代表所求的和。 样例输入 4 3 1 2 3 4样例输出 9
-  解题思路 这道题总共有两种解法,第一种就是枚举法(不是暴力枚举),第二种则是背包问题动态规划。先介绍第一种,我们在乎的其实就是模 k k k的余数以及其数的大小。那么试想,如果有超过 3 3 3个的且模 k k k相同的数,我们怎么选择,如果要想和最大,我们肯定会选择其中最大的三个来试探(因为选取最多就是三个),所以如果我们不舍弃的话就会做很多无用功。那么处理的方法就是按模 k k k的余数分类存储,然后分组排序,取前三个。然后就是可以直接枚举两个余数来确定另一个余数即可了,这种方法很好理解。那第二种就是背包问题解法了,按照我们上面的优化,我们还是取前三个数,不过现在不同了,涉及了一个选不选的问题,我们可以定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示已经选了 i i i个数且余数为 j j j的当前最大和,那么状态转移方程就自然易得了: d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ ( ( j − x ) dp[i][j]=max(dp[i][j],dp[i-1][((j-x)%K+K)%k]+x) dp[i][j]=max(dp[i][j],dp[i−1][((j−x)。 
-  枚举优化代码 
/**
  *@filename:倍数问题
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-07 22:06
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
int n,mod;//n个数与mod。
void solve(){
}
int main(){
    while(cin>>n>>mod){
        vector<vector<int> > nums(mod); 
        int temp;
        for(int i=0;i<n;i++){
            cin>>temp;
            nums[temp%mod].push_back(temp);
        }
        //排序。
        for(int i=0;i<mod;i++){
            if(nums.size())sort(nums[i].begin(),nums[i].end(),greater<int>());//降序排列。
        }
        //接下来开始枚举即可。枚举两个余数确定另一个余数。
        int maxx=0;
        for(int i=0;i<mod;i++){
            for(int j=0;j<mod;j++){
                int k=((mod-i-j)%mod+mod)%mod;
                //枚举之后确定是否可行。
                if(nums[i].size()&&nums[j].size()&&nums[k].size()){
                    //接下来分情况选择。
                    int ans=0;
                    if(i!=j&&i!=k&&j!=k){
                        ans+=nums[i][0]+nums[j][0]+nums[k][0];
                    }
                    else if(i==j&&j==k){
                        if(nums[i].size()>=3){
                            ans+=nums[i][0]+nums[i][1]+nums[i][2];
                        }
                    }
                    else if(i==j){
                        if(nums[i].size()>=2){
                            ans+=nums[i][0]+nums[i][1]+nums[k][0];
                        }
                    }
                    else if(i==k){
                        if(nums[i].size()>=2){
                            ans+=nums[i][0]+nums[i][1]+nums[j][0];
                        }
                    }
                    else if(j==k){
                        if(nums[j].size()>=2){
                            ans+=nums[i][0]+nums[j][0]+nums[j][1];
                        }
                    }
                    maxx=max(maxx,ans);
                }
            }
        }
        cout<<maxx<<endl;
    }
    return 0;
}
- 动态规划优化代码
/**
  *@filename:倍数问题-动态规划
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-08 12:20
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int n,K;
int dp[4][1005];
void solve(){
}
int main(){
    while(cin>>n>>K){
        vector<vector<int> > nums(K);
        int temp;
        for(int i=0;i<n;i++){
            cin>>temp;
            nums[temp%K].push_back(temp);
        }
        for(int i=0;i<K;i++){
            if(nums[i].size())sort(nums[i].begin(),nums[i].end(),greater<int>());//降序排列。
        }
        memset(dp,-0x3f3f,sizeof(dp));
        dp[0][0]=0;//没有选取任何数,自然为0.
        for(int index=0;index<K;index++){
            for(int i=0;i<3&&i<nums[index].size();i++){
                int x=nums[index][i];
                for(int j=3;j>=1;j--){
                    for(int k=0;k<K;k++){
                        dp[j][k]=max(dp[j][k],dp[j-1][((k-x)%K+K)%K]+x);
                    }
                }
            }
        }
        cout<<dp[3][0]<<endl;
    }
    solve();
    return 0;
}
第十题 付账问题
-  问题重现 几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。 
 现在有 n 个人出去吃饭,他们总共消费了 S 元。其中第 i 个人带了 ai 元。
 幸运的是,所有人带的钱的总数是足够付账的。但现在问题来了:每个人分别要出多少钱呢?
 为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。
 这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是1分钱的整数倍。你需要输出最小的标准差是多少。
 标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
 形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 :
  输入 第一行包含两个整数 n、S; 
 第二行包含 n 个非负整数 a1, …, an。
 n ≤ 5 × 10^5, 0 ≤ ai ≤ 10^9。输出 输出最小的标准差,四舍五入保留 4 位小数。 
 保证正确答案在加上或减去 10^−9 后不会导致四舍五入的结果发生变化。样例输入 10 30 2 1 4 7 4 8 3 6 4 7样例输出 0.7928
-  解题思路 这道题就是纯粹的贪心题,由于平均值已经确定了,所以我们就是想让每个人都尽量接近平均值。 那么我们可以对这金额进行排序,从小到大遍历,如果低于平均值,那么肯定是要全部支付的,那么不够的钱则是由剩下的人来平摊,所以这里我们需要更新平均值。而如果高于,就只需要支付剩下的人需要平摊的钱即可。注意要实时更新平均值,也就是剩下的人需要平摊的钱数。 
-  代码 
/**
  *@filename:付账问题
  *@author: pursuit
  *@CSDNBlog:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-04-08 12:51
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500000 + 10;
const int mod = 1e9+7;
int n;
long double s;
int a[maxn];
int main(){
    while(cin>>n>>s){
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        long double ave,sum;
        ave=s/n,sum=0;
        double cur_pay;
        sort(a,a+n);
        for(int i=0;i<n;i++){
            cur_pay=s/(n-i);//获取剩下的人需要平摊的钱数。
            if(a[i]<cur_pay){
                //如果低于此时每个人都需要支付的数,那么就全部付出,并更新剩下的人所需平摊的值。
                cur_pay=a[i];
            }
            //如果高于,就只需支付现在每个人所需的平摊数。
            s-=cur_pay;
            sum+=(cur_pay-ave)*(cur_pay-ave);
        }
        printf("%.4Lf\n",sqrt(sum/n));
    }
    return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号