2016.12.05 蓝桥杯校赛题解

来源:互联网 发布:电脑编程c语言书籍 编辑:程序博客网 时间:2024/06/18 16:49

(搬一下ak小锋的题解)

ProblemA 神秘的三位数(代码填空)

问题描述 有这样⼀一个3位数,组成它的3个数字阶乘之和正好等于它本身。即:abc =a! +b! +c! 下⾯面的程序⽤用于搜索这样的3位数。请补全缺失的代码。

解题思路 模拟,将所有三位数的数字阶乘算出来和原数字⽐比较即可找出,题中阶乘数组已给,因此空 格部分代码为sum+=jc[x%10];

Problem B 用一个正整数产⽣生一个回文数

问题描述 若⼀一个数(⾸首位不不为0)从左到右读与从右到左读都是⼀一样,这个数就叫做回⽂文数,例例如12521 就是⼀一个回⽂文数。 给定⼀一个正整数,把它的每⼀一个位上的数字倒过来排列列组成⼀一个新数,然后与原数相加,如 果是回⽂文数则停⽌止,如果不不是,则重复这个操作,直到和为回⽂文数为⽌止。给定的数本身不不为 回⽂文数。 例例如,给定⼀一个正整数87则有: STEP1: 87+78=165 STEP2: 165+561=726 STEP3: 726+627=1353 STEP4: 1353+3531=4884 编写⼀一个程序,输⼊入M(12<=M<=1000) ,输出最少经过⼏几步可以得到回⽂文数。如果在8步以 内(含8步)不不可能得到回⽂文数,则输出0。

解题思路 暴力模拟,记录步数

AC 代码

include<bits/stdc++.h> using namespace std; int f(int a) {     int b=0;     while(a)     {         b=b*10+a%10;         a/=10;     }     return b; } int main(){    intT;     scanf("%d",&T);     while(T--)     {         int a;         scanf("%d",&a);         int ans=0;         while(ans<=8)         {             ans++;             a+=f(a);             if(a==f(a)) break;         }         if(ans>8) ans=0;         printf("%d\n",ans);     }    return 0;}

Problem C 自守数

问题描述 自守数是指⼀一个数的平⽅方的尾数等于该数⾃自身的⾃自然数。例例如: 25^2=625 76^2=5776 9376^2=87909376 (其中的^表示次方,如:25^2 表示25的平方) 。 给你一批2147483647以内 的正整数,判断它们是不不是自守数,是输出Y,不不是输出N。例如: 输入:67276625,则必须输出:YNNY

解题思路 模拟,爆int 就用long long 嘛,a平方算出来,位数记录一下,对a平方取个模和原数比较即可。

AC 代码

#include<bits/stdc++.h> using namespace std; int main() {     int a;     while(scanf("%d",&a)!=EOF)     {         if(!a)         {             printf("Y");             continue;        }         long long b=a;         b=b*b;         long long c=1,d=a;         while(a)         {             c*=10;             a/=10;         }         if(b%c==d) printf("Y");         else printf("N");    }     return 0;}

Problem D 高兴天数

问题描述 小X性格很独特,如果她今天高兴度比上次⼀一样或更高,她就会很善良,相反,如果她今天高兴度比上次低,她就会很凶!现在已经知道小X在N天⾥里里每天的高兴度M。根据这N天 中她每天高兴度M,合理安排与她相处时间,使大家与小X友好相处尽量多天数。现在要 求计算出最多能和小X友好相处多少天。

解题思路 LIS,⽤用一个数组记录每个长度的最小尾数,遍历数据数组,二分出这个数作为尾数的最长子序列长度,并更新尾数数组与答案,时间复杂度O(nlogn)

AC 代码

#include<bits/stdc++.h> using namespace std; int data[30005]={0},d[30005]; int main() {     int n;     scanf("%d",&n);     for(int i=1;i<=n;i++)        scanf("%d",&data[i]);     for(int i=1;i<=n;i++)         d[i]=35000;     int ans=0;     for(int i=1;i<=n;i++)     {         int p=upper_bound(d+1,d+n+1,data[i])-d;        d[p]=data[i];         ans=max(ans,p);     }    printf("%d\n",ans);     return 0;}

Problem E 作弊的发牌者

问题描述 lyt 正在与他的N-1(2<=N<=100)个朋友打牌。他们玩的牌一副为K(N<=K<= 100,000,K 为N的倍数)张。所有牌中,一共有M(M =K/N)张“好牌”,其余的K-M张为“差牌”。lyt是游戏的发牌者,很自然地,他想把所有好牌都留给自己。他热衷于获胜,即使为此必须采取一 些不正当的手段。 在若干局游戏后,lyt的朋友们开始怀疑lyt在游戏中作弊,于是他们想了个对策:使用新的发牌规则。规则具体如下: 1. lyt把牌堆的最上面一张发给他右边的单身狗; 2. 每当lyt 发完一张牌,他都得将牌堆顶部接下来的P(1<=P<=10)张牌放到底部去(一般把这个操作称为切牌) ; 3. 然后,lyt 对逆时针方向的下一头单身狗重复上述的操作; lyt 绝望地认为,他再也不不可能获胜了,于是他找到了你,希望你告诉他,将好牌放在初始牌堆的哪些位置,能够确保它们在发完牌后全集中到他手里。顺带说明一下,我们把牌堆顶的牌定义为1号牌,从上往下第二张定义为2号牌,依此类推。

解题思路 数据结构模拟,然后把自己被发到的牌丢进vector 排个序输出就好了了,时间复杂度O(kp)。 我刚开始想的双向链表,赛后才想到队列,比双向链表好写多了,太蠢了。那就给双向链表 代码随便看看好了。

AC 代码

#include<bits/stdc++.h> using namespace std; vector<int>V; struct list {     int a,nex,pre; }data[100005]; int main() {     int n,k,p;     scanf("%d%d%d",&n,&k,&p);     for(int i=1;i<=k;i++)     {         data[i].pre=i-1;         data[i].a=i;         data[i].nex=i+1;     }    data[k].nex=1;     data[1].pre=k;     int t=k/n;     int no=1;     for(int i=0;i<t;i++)     {         for(int j=0;j<n-1;j++)         {             int pre=data[no].pre;             int nex=data[no].nex;             data[pre].nex=nex;             data[nex].pre=pre;             no=data[no].nex;             for(int l=0;l<p;l++) no=data[no].nex;         }        V .push_back(no);         int pre=data[no].pre;         int nex=data[no].nex;         data[pre].nex=nex;         data[nex].pre=pre;         no=data[no].nex;         for(int l=0;l<p;l++) no=data[no].nex;     }     sort(V .begin(),V .end());     for(int i=0;i<V .size();i++) printf("%d\n",V[i]);     return 0;}

Problem F 朋友

问题描述 有一个城镇,住着n个市⺠民。已知一些⼈人互相为朋友。引用一个名人的话说,朋友的朋友也是朋友。意思是说如果A和B是朋友,C和B是朋友,则A和C是朋友.你的任务是数出最大朋友组的人数。

解题思路 并查集,维护所有节点对应的集合,并且在根节点记录集合大小,每次合并更新并查集状态并更更新答案即可。

AC 代码

include<bits/stdc++.h> using namespace std; int fa[30005],cnt[30005]; int findf(int a){    if(a==fa[a]) return a;     else return fa[a]=findf(fa[a]);} int main() {     int n,m;     scanf("%d%d",&n,&m);     for(int i=1;i<=n;i++)     {        fa[i]=i;         cnt[i]=1;     }     int ans=0;     while(m--)     {         int a,b;         scanf("%d%d",&a,&b);         int aa=findf(a),bb=findf(b);         if(aa!=bb)         {             fa[bb]=aa;             cnt[aa]+=cnt[bb];             ans=max(ans,cnt[aa]);         }     }      printf("%d\n",ans);      return 0; }

Problem G 连接格点

问题描述 有一个M行N列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。

解题思路 因为边最多1000×999×2条,点最多1000×1000个,可以直接用kruskal 做最小生成树。

AC 代码

#include<bits/stdc++.h> using namespace std; int fa[2000005];int findf(int a) {     if(a==fa[a]) return a;     else return fa[a]=findf(fa[a]); } int main() {     int n,m;     scanf("%d%d",&n,&m);     for(int i=1;i<=2000005;i++) fa[i]=i;     int x1,y1,x2,y2,ans=0;     while(scanf("%d%d%d%d",&x1,&y1,&x2,&y2)!=EOF)     {         int a=x1*1005+y1;         int b=x2*1005+y2;         int aa=findf(a),bb=findf(b);         if(aa!=bb) fa[aa]=bb;     }    for(int i=1;i<n;i++)        for(int j=1;j<=m;j++)         {             int a=i*1005+j;             int b=a+1005;             int aa=findf(a),bb=findf(b);             if(aa!=bb) fa[aa]=bb,ans++;         }     for(int i=1;i<=n;i++)        for(int j=1;j<m;j++)         {             int a=i*1005+j;             int b=a+1;             int aa=findf(a),bb=findf(b);            if(aa!=bb) fa[aa]=bb,ans+=2;         }     printf("%d\n",ans);     return 0; }

Problem H 木棍

问题描述 在一个原始部落,有一些人要去打猎了了,每个人都要挑选自己的工具——两根木棍。一 个用作远距离投掷攻击,一个用作近距离搏斗。但是每个人都想挑到最好的,但这是不可能的。但是为了让多数⼈人满意,也为了减少大家的矛盾。部落领袖设计了一个矛盾指数,这个 指数就是每个人的不舒服指数和,不舒服指数就(L1-L2)^2(^代表次⽅方) ,其中L1,L2 分别是一个人的两根木棍的长度。
部落领袖决定让矛盾指数最少,于是他来向你寻求帮助,希望你能告诉他矛盾指数至少有多少。

解题思路 先对木棍长度排序,可以证明最优情况一定是所有人选择了相邻长度的木棍。接下来使用动态规划算法求解,dp[i][j]表示第i个⼈人取到第j根木棍的最小答案,状态转移方程为dp[i][j] 等于dp[i-1][0 到j-2]的最小值加上data[j]-data[j-1]的平方,做到i=n 时更新答案。

AC 代码

#include<bits/stdc++.h> using namespace std; int data[2005]; int dp[505][2005]; int main() {     int m,n,ans=0x3f3f3f3f;     scanf("%d%d",&m,&n);     for(int i=1;i<=m;i++) scanf("%d",&data[i]);    sort(data+1,data+1+m);     memset(dp,0x3f3f3f3f,sizeof(dp));     dp[0][0]=0;     for(int i=1;i<=n;i++)     {         int mi=dp[i-1][i*2-2];         for(int j=i*2;j<=m;j++)         {             mi=min(mi,dp[i-1][j-2]);             dp[i][j]=mi+(data[j]-data[j-1])*(data[j]-data[j-1]);             if(i==n) ans=min(ans,dp[i][j]);         }    }     printf("%d\n",ans);     return 0; }

Problem I 采药

问题描述 xgs 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最 有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间, 每一株也有它⾃自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。 ”如果你是xgs,你能完成这个任务吗?

解题思路 裸01背包,但是N,M太大做不了,我们观察到所有草药花费的时间和价值都在10以内,因此我们可以把所有草药中花费时间和价值一样的合并,因此问题变成了了N为100(0无意义过滤掉),M为100000的多重背包,二进制优化T掉了。我们发现,对于背包容量%当前物品重量相等的情况,我们可以一起更新,并使⽤用单调队列优化,即可将时间复杂度优化成 O(NM),相对于二进制优化省了一个log,就能过了。

AC 代码

#include<bits/stdc++.h> #define mp make_pair using namespace std; int data[15][15]={0}; int dp[100005]; pair<int,int> que[100005]; int main() {     int ll=0,rr=0;     int n,m;     scanf("%d%d",&n,&m);     while(n--)     {         int a,b;         scanf("%d%d",&a,&b);         data[a][b]++;     }     for(int i=0;i<=10;i++)    for(int j=0;j<=10;j++)        if(data[i][j])         {             for(int k=0;k<i;k++)             {                 ll=rr=0;                 que[rr++]=mp(dp[k],0);                 for(int l=1;l*i+k<=m;l++)                 {                     int nu=l*i+k;                    while(ll<rr&&que[ll].first+(l-que[ll].second)*j<dp[nu]) rr--;                     que[rr++]=mp(dp[nu],l);                    while(l-que[ll].second>data[i][j]) ll++;                     dp[nu]=que[ll].first+(l-que[ll].second)*j;                  }             }         }     printf("%d\n",dp[m]);     return 0;}

Problem J 三个袋子

问题描述

题目背景:cwz 在公园里游玩时捡到了很多小球,而且每个球都不一样。cwz 找遍了全身只发现了了3个一模一样的袋子。他打算把这些小球都装进袋子里(袋子可以为空) 。他想知道他总共有多少种放法。
问题描述: 将N个不同的球放到3个相同的袋子里,求放球的方案总数M。 结果可能很大,我们仅要求输出Mmod K的结果。 现在,平平已经统计出了了N<=10 的所有情况。⻅见下表: N 1 2 3 4 5 6 7 8 9 10 M 1 2 5 14 41 122 365 1094 3281 9842

解题思路 容易得出m[i]=m[i-1]*3-1,在m[1]=1 的情况下,可推出通项公式m[n]=(3^(n-1)+1)/2%k。由 于 2与 k 不一定互质,因此不不能转成乘法逆元。我们容易得出 a/b%k=a%(b×k)/b,因此 m[n]=(3^(n-1)%2k+1)/2,n比较大的情况考虑快速幂,时间复杂度O(logn)。

AC 代码

#include<bits/stdc++.h> using namespace std; long long kpow(int a,int b,intc) {     long long ans=1;     long long tmp=a;     while(b)     {         if(b&1) ans=ans*tmp%c;         tmp=tmp*tmp%c;         b>>=1;     }     return ans; } int main() {     int n,k;     scanf("%d%d",&n,&k);     printf("%lld\n",(kpow(3,n-1,k<<1)+1)>>1);     return 0; }

Problem K 广告收入

问题描述 大家都知道,lwx 的OI 商店是靠广告来获得收入的。而广告都是那些热心的顾客们点击的。每点击一次,Google就会给lwx的账户中存一定的钱。当该⽉月的收入大于等于100美元时,Google 才会把账户里的钱寄给lwx,然后lwx 就可以拿着这笔钱去资助失学儿童了! Google会在OI 商店的网页上随机的发布N家广告商的广告,每家广告商的广告的价格 (即Google支付给lwx的钱数)是不同的。这个月有M个热心的顾客点击了广告。 假如说资助一个失学儿童需要K美元的话。现在已经知道了了这N家⼴广告商的价格,也 知道了了M个热心的顾客都点了了哪些广告。请你帮lwx算一下,他最多能资助几个失学儿童呢?

解题思路 用map 存下广告价格,然后直接计算即可。

AC 代码

#include<bits/stdc++.h> using namespace std; map<string,double>M; int main() {     int n,m,k;     cin>>n>>m>>k;     while(n--)     {         string a;         double b;         cin>>a>>b;         M[a]=b;     }     double ans=0.0;     while(m--)     {         string a,b;         cin>>a>>b;         ans+=M[b];     }     cout<<floor(ans/k)<<endl;     return 0; }

Problem L 招聘

问题描述 Alice新开了一家公司,它的下面有两个项目,分别需要N1和N2个⼈人来完成。现在有N个人前来应聘,于是Alice通过⾯面试来决定他们中的哪些⼈人会被录用。
Alice在面试中,会仔细考察他们能如何为公司的项目带来收益。她给每个人打了两个分值Q1和Q2,表示他加入第一个和第二项目分别能带来的收益值。同时,她也会仔细考察他们每个人的缺点,并且给每人打了另两个分值C1和C2,表示他们进入每个项目可能带来的负面效应。Alice心目中的最优决策是,在决定好录用哪些人以及每个人在哪个项目下工作之后,他们为公司带来的收益总和,除以他们为项目带来的负面效应总和,这个比值要最大。 你能帮他计算出在最优决策下,这个比值为多少吗? 前来应聘的人数总是大于等于两个项目需求人数的总和,因此 Alice 一定会恰好招 N1+N2 个人,分配给第一个项目N1个人,分配给第二个项目N2个人,没有人会同时属于两个项目。

解题思路 对于答案r,存在一种分配⽅方案,使得Q的总和等于r乘上C的总和,根据这个性质二分答 案,使⽤用动态规划法求出对应 r 的最大 Q 的总和-r 乘上 C 的总和,dp[i][j][k]表示做到前 i 个人,j个人招去做项目1,k个人招去做项目2情况下上述的最大答案,dp[i][j][k]为以下三种情况的最大值 dp[i-1][j][k](不招这个人) dp[i-1][j-1][k]+data[i].q1-data[i].c1*r(招这个人做项目1) dp[i-1][j][k-1]+data[i].q2-data[i].c2*r(招这个人做项目2)

AC 代码

#include<bits/stdc++.h> using namespace std; struct p {     double q1,q2,c1,c2;     void input()     {         scanf("%lf%lf%lf%lf",&q1,&c1,&q2,&c2);     } }data[55]; double dp[55][55][55]; int n,n1,n2; bool isok(double r) {     for(int i=0;i<55;i++)    for(int j=0;j<55;j++)    for(int k=0;k<55;k++)         dp[i][j][k]=-999999999.0;     dp[0][0][0]=0;     for(int i=1;i<=n;i++)     {         for(int j=0;j<=n1;j++)        for(int k=0;k<=n2;k++)         {             if(j+k>i) break;             dp[i][j][k]=dp[i-1][j][k];             if(j==0&&k==0);             else if(k==0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+data[i].q1-data[i].c1*r);            else if(j==0)dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+data[i].q2-data[i].c2*r);             else             {                 dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+data[i].q1-data[i].c1*r);                 dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+data[i].q2-data[i].c2*r);             }        }    }     return dp[n][n1][n2]>0.0;} int main() {     intT;     scanf("%d",&T);     for(int cas=1;cas<=T;cas++)     {         memset(dp,0,sizeof(dp));         scanf("%d%d%d",&n,&n1,&n2);         for(int i=1;i<=n;i++) data[i].input();         double l=0.0,r=20000.0;         while(r-l>0.000001)         {             double mid=(l+r)/2.0;             if(isok(mid))l=mid;             else r=mid;         }         printf("Case #%d: %.6lf\n",cas,l);     }     return 0; }
0 0