2015蓝桥杯初赛


方程整数解

题意:

给定n,求出 a^2 + b^2 + c^2 = n(1<=n<=10000)的所有解,解要保证c>=b>=a>=1。

思路:

枚举a和b,利用a和b推c,判断是否合法即可

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n;
    while(cin>>n){
        int ok=0;
        for(int a=1;a*a<n;a++){
            for(int b=a;a*a+b*b<n;b++){
                int cc=n-(a*a+b*b);
                int c=sqrt(cc);
                if(c>=b&&c*c==cc){
                    cout<<a<<' '<<b<<' '<<c<<endl;
                    ok=1;
                }
            }
        }
        if(!ok)cout<<"No Solution"<<endl;
    }
    return 0;
}


星系炸弹

题意:

有一个贝塔炸弹,a年b月c日放置,定时为n天,请你计算它爆炸的准确日期。
n<=1e3

思路:

挺烦日期题的。
因为n比较小,只有1000,所以可以直接一天一天加,很稳。

code:

#include<bits/stdc++.h>
using namespace std;
int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};//每个月的天数
bool check(int n){//判断闰年
    if(n%4==0&&n%100!=0)return 1;
    if(n%400==0)return 1;
    return 0;
}
signed main(){
    int a,b,c,n;
    while(cin>>a>>b>>c>>n){
        while(n--){
            if(check(a))s[2]=29;//如果是闰年则2月有29天
            else s[2]=28;
            c++;
            if(c>s[b])c=1,b++;
            if(b>12)b=1,a++;
        }
        printf("%04d-%02d-%02d\n",a,b,c);
    }
    return 0;
}

奇妙的数字

题意:

一个数的平方和立方正好把0~9的10个数字每个用且只用了一次。你能猜出这个数字是多少吗?

思路:

因为这个数一定存在,写个程序直接while(1)从小到大不断枚举数x,然后把x2和x3拆位判断是否每种数字都出现一次就行了。
最后计算出答案为69。


牌型种数

题意:

从52张牌取13张,如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序
自己手里能拿到的初始牌型组合一共有多少种呢?

思路:

因为只需要输出答案,直接枚举每种点数的牌的数量,可以dfs也可以直接13个for循环。
答案为3598180。


手链样式

题意:

小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙。
他想用它们串成一圈作为手链,送给女朋友。
现在小明想知道:如果考虑手链可以随意转动或翻转,一共有多少不同的组合样式?

思路:

因为只需要输出答案,所以可以不想那么多,直接开map标记+全排列计算答案。
注意题目说明可以随意转动和翻转,别漏了。
算出答案为1170。

去别人那找个了组合数学的结论,顺便挂下:
在这里插入图片描述

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
map<string,int>mark;
signed main(){
    string s="aaabbbbccccc";//3+4+5=12
    int ans=0;
    do{
        string t=s;
        if(!mark[t])ans++;
        for(int i=0;i<12;i++){//把翻转和循环都标记掉
            mark[t]=1;
            reverse(t.begin(),t.end());//翻转
            mark[t]=1;
            reverse(t.begin(),t.end());//翻回来
            //循环一下,把第一个字符放到最后一个
            char d=*t.begin();//取开头
            t.erase(t.begin());//删掉开头
            t+=d;//放到结尾
        }
    }while(next_permutation(s.begin(),s.end()));
    cout<<ans<<endl;
    return 0;
}

饮料换购

题意:

开始有n瓶饮料,3个瓶盖可以换一瓶,问最后一共喝到多少瓶。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long

signed main(){
    int n;
    while(cin>>n){
        int ans=n;
        //n为瓶盖数
        while(n>=3){
            int t=n/3;//换到的饮料数
            n-=t*3;//瓶盖减少
            ans+=t;//累加答案
            n+=t;//瓶盖增加
        }
        cout<<ans<<endl;
    }
    return 0;
}

垒骰子(矩阵快速幂)

题意:

赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 1e9+ 7 的结果。

思路:

非常容易就能想到dp的解法,但是硬莽的话只能过百分60的数据(n<=100)。
对于百分100的数据,n<=1e9,数据这么大肯定是往快速幂方向想,应该是矩阵快速幂。
d(i,j)表示i个骰子,第j面朝上的做法,显然d(i,j)=sigma d(i-1,k),其中j的对立面和k不互斥。
发现这个递推关系只有最多6个加法,可以直接一次6阶矩阵转移计算出d(i),配合矩阵快速幂就很快了。
利用互斥关系构造转移矩阵,矩阵快速幂即可。
注意每个固定住上下面的骰子可以可以旋转,因此答案要乘上4n(4个面)。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=6;
const int mod=1e9+7;
struct Node{
    int a[N][N];
};
int g[6][6];
int dif[6]={3,4,5,0,1,2};//每个数对面的数
Node mul(Node A,Node B){
    Node C;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)C.a[i][j]=0;
    for(int k=0;k<N;k++)for(int i=0;i<N;i++)if(A.a[i][k]){
        for(int j=0;j<N;j++)if(B.a[k][j]){
            C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
        }
    }
    return C;
}
Node Pow(Node a,int b){
    Node ans;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)ans.a[i][j]=(i==j);
    while(b){
        if(b&1)ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
int ppow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
signed main(){
    int n,m;
    while(cin>>n>>m){
        for(int i=0;i<6;i++)for(int j=0;j<6;j++)g[i][j]=1;
        for(int i=1;i<=m;i++){//输入m对互斥关系
            int a,b;
            cin>>a>>b;
            a--,b--;
            g[a][b]=g[b][a]=0;
        }
        Node x;
        for(int i=0;i<6;i++)for(int j=0;j<6;j++)x.a[i][j]=0;
        for(int i=0;i<6;i++)for(int j=0;j<6;j++){
            if(g[i][j]){//dif[j]可从i转移
                x.a[i][dif[j]]=1;
            }
        }
        Node p=Pow(x,n-1);
        int ans[6]={0};//ans[i]即d[n][i]
        for(int i=0;i<6;i++){//计算ans[],因为初始矩阵为1*6的全1矩阵,所以ans[i]就是x的第i列相加
            for(int j=0;j<6;j++){
                ans[i]+=p.a[i][j];
            }
        }
        int sum=0;
        for(int i=0;i<6;i++)sum=(sum+ans[i])%mod;
        sum=sum*ppow(4,n)%mod;
        cout<<sum<<endl;
    }
    return 0;
}

灾后重建(压轴题)

题意:

给定n个点m条带权边的无向图
q组询问,每组询问问(L,R,K,C),要求输出编号在[L,R]之间,且编号模K等于C的点,现在要你选出一些边,使得这些点连通,问选出的边权的最大值最小为多少(保证每次询问至少存在两个点)。
(n<=5e4,m<=2e5,q<=5e5)
(L,R,K均在[1,n]以内,C在K以内)
时限5s

思路:

网上搜到的分级数据:
百分30数据nmq<=30
百分60数据nmq<=2000
百分100数据n<=5e4,m<=2e5,q<=1e6

最小生成树能使得所有点联通,因此先构建出最小生成树,记录树边,其他多余的边没有用。

百分30:
做法1.能使得所有点联通的就是最小生成树,对于每组询问 ,标记模k等于c的点,然后用已经记录的树边跑kruskal,如果在连接某个边之后,标记的点能够联通,那么这个点的边权就是答案。
做法2.二分最大边权的最小值,然后在最小生成树上从任意标记的点开始dfs,只走边权小于等于当前二分的值的路径,最后判断能否把标记的点全部搜到。

百分60:
1.最小生成树+LCA,在最小生成树上记录任意两点之间路径的最大值
2.kruskal重构树+LCA,也是记录两点之间最大路径最小值
对于每组询问,对任意两点之间的路径最大值取max就是答案

百分100:
建议直接放弃


奖券数目

题意:

计算10000-99999中有多少个不带4的数字,直接输出答案

思路:

遍历+拆位暴力判断即可


三羊献瑞

题意:

在这里插入图片描述

思路:

全排列枚举每种汉字所代表的数,判断是否合法即可

code:

#include<bits/stdc++.h>
using namespace std;
int a[10]={0,1,2,3,4,5,6,7,8,9};
//祥瑞生辉三羊献气
signed main(){
    do{
        if(a[0]&&a[4]){
            int aa=a[0]*1000+a[1]*100+a[2]*10+a[3];
            int bb=a[4]*1000+a[5]*100+a[6]*10+a[1];
            int sum=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
            if(aa+bb==sum){
                cout<<bb<<endl;
                break;
            }
        }
    }while(next_permutation(a,a+10));
    return 0;
}

加法变乘法

题意:

我们都知道:1+2+3+ … + 49 = 1225
现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015
比如:
1+2+3+…+1011+12+…+2728+29+…+49 = 2015 就是符合要求的答案。
请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交。

思路:

枚举两个乘号的位置,判断是否合法即可。
计算出答案为16,另写一个程序直接输出。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n=49;
    for(int i=1;i<n;i++){//第一个乘号在i之后
        for(int j=i+2;j<n;j++){//第二个乘号在j之后
            int sum=0;
            for(int k=1;k<=i-1;k++)sum+=k;
            sum+=i*(i+1);
            for(int k=i+2;k<=j-1;k++)sum+=k;
            sum+=j*(j+1);
            for(int k=j+2;k<=n;k++)sum+=k;
            if(sum==2015)cout<<i<<endl;
        }
    }
    return 0;
}

移动距离

题意:

在这里插入图片描述

思路:

这题的题面有点误导人,看题可能会以为只能沿着那个数字顺序走,其实是可以直接上下左右走的。
因此这题其实就是计算两个点的笛卡尔距离,算出坐标差即可。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int w,m,n;
int getr(int x){//计算在第几行
    return x/w+(x%w!=0);
}
int getc(int x){//计算在第几列
    int t=getr(x);
    x%=w;
    if(t%2){//奇数正向
        return x;
    }else{//反向
        return w-x+1;
    }
}
signed main(){
    while(cin>>w>>m>>n){//w为一行的宽度
        int ans=abs(getr(m)-getr(n));
        ans+=abs(getc(m)-getc(n));
        cout<<ans<<endl;
    }
    return 0;
}

生命之树(树形dp)

题意:

给定一颗n个节点的树,每个点有点权
现在要求选出一个点集,满足:
点集之内的任意两点之间的路径上的点也在这个点集中
问选出的点集的最大点权和是多少

思路:

首先要看懂题,题意就是选择一些点,去掉其他点之后,选择的这些点在同一个连通块中,求最大点权和
令d(i)为选择点i所能得到的最大值,树形dp:
考虑子节点v,如果d(v)大于0,就选择v
考虑父节点,如果选择父节点能使得答案变大,那么d(x)就是答案,回溯到x的时候就能更新出更大的d(x)>d(v)
所以不需要管父节点,并不失完备性。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int a[maxm];
int d[maxm];
void dfs(int x,int fa){
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
        if(d[v]>0)d[x]+=d[v];
    }
    d[x]+=a[x];
}
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,-1);
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,d[i]);
    }
    cout<<ans<<endl;
    return 0;
}

打印大X

题意:

小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度。
为了便于比对空格,所有的空白位置都以句点符来代替。
要求输入两个整数m n,表示笔的宽度,X的高度。

思路:

观察样例发现X是对称的,所以计算上面一半,下面一半倒着输出就行了。
这题麻烦的点是他只给行数n,不给列数,要你自己算。
观察样例图形可以发现,第n/2+1行星号的长度为x,每向上一行就向左右拓展两格
所以列数为n/2*2+m。
后面的图形打印就没什么好说的了。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e3+5;
char a[maxm][maxm];
signed main(){
    int x,n;
    while(cin>>x>>n){//x是X的宽度
        int m=n/2*2+x;//列
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=0;
        int space=0;//前后的空格长度
        for(int i=1;i<=n/2+1;i++){
            for(int j=space+1;j<=space+1+x-1;j++){
                a[i][j]='*';
            }
            for(int j=m-space;j>=m-space-x+1;j--){
                a[i][j]='*';
            }
            space++;
        }
        for(int i=1;i<=n/2+1;i++){
            for(int j=1;j<=m;j++){
                if(!a[i][j])a[i][j]='.';
                cout<<a[i][j];
            }
            cout<<endl;
        }
        for(int i=n/2;i>=1;i--){
            for(int j=1;j<=m;j++){
                cout<<a[i][j];
            }
            cout<<endl;
        }
    }
    return 0;
}

发布了430 篇原创文章 · 获赞 36 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/104563244