蓝桥杯(写给2022备战的同学)(历年题解)

主要放上了有意义的题,所以还是可以看一下的

方程整数解

方程: a^2 + b^2 + c^2 = 1000 
这个方程有正整数解吗?有:a,b,c=6,8,30 就是一组解。  
求出 a^2 + b^2 + c^2 = n(1<=n<=10000)的所有解,解要保证c>=b>=a>=1。

思路:三个未知量,三个循环的话必然会超时,所以进行优化,c^2 = n-a*a-b*b

所以我们只用枚举a,b,然后对c进行判断就可以了

#include<bits/stdc++.h>
using namespace std;
int n;
int check(int i,int j){
	int t = n-(i*i)-(j*j);
	int w = sqrt(t);
	if(w*w==t&&w>=j)return w;
	return -1;
}
int main(){
	
	while(cin>>n){

	int flag =0;
	for(int i = 1;i<=n;i++){
		for(int j = i;j<=n;j++){
			int t;
			if(i*i+j*j>=n)break;
			if((t=check(i,j))!=-1){
				flag=1;
				cout<<i<<" "<<j<<" "<<t<<endl;
			}
		}
	}
	if(!flag)puts("No Solution");
}
	return 0;
} 

奇妙的数字

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

思路:每个数只用一次,所以这个数的三次方在10000000000以内,即这个数在100以内

所以我们只用枚举100以内的数

#include <iostream>
using namespace std;
 
bool judge(int x, int y)
{
    int a[10] = {0};
    while(x)
    {
        a[x % 10] ++;
        x /= 10;
    }
    while(y)
    {
        a[y % 10] ++;
        y /= 10;
    }
    for (int i = 0; i < 10; i ++) if(a[i] != 1) return false;
    return true; 
}
 
int main()
{
    for (int i = 32; i < 100; i ++) 
    {
        int a = i * i, b = i * i * i;
        if(judge(a, b)) cout << i << endl;
    } 
     
    return 0;
}

饮料换购

乐羊羊饮料厂正在举办一次促销优惠活动。
乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去(但不允许暂借或赊账)。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动。
那么,对于他初始买入的n瓶饮料,最后他一共能喝到多少瓶饮料。

思路:简单的模拟

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

int main(){
	int n;
	while(cin>>n){
		int cnt= 0 ;
		cnt = n;
		while(n>=3){
			if(n>=3){
				n-=3;
				n++;
				cnt++;
			}
		}
		cout<<cnt<<endl;
	}
	

} 

星系炸弹

在X星系的广袤空间中漂浮着许多X星人造“炸弹”,用来作为宇宙中的路标。
每个炸弹都可以设定多少天之后爆炸。
比如:阿尔法炸弹2015年1月1日放置,定时为15天,则它在2015年1月16日爆炸。
有一个贝塔炸弹,a年b月c日放置,定时为n天,请你计算它爆炸的准确日期。

输入:
2015 1 1 15
2014 11 9 1000

输出:
2015-01-16
2017-08-05

思路:(其实看看代码就能懂)因为数据量较小,所以直接让天数从1开始遍历到n,每次当前的天数>当月的合法天数时,

更新当前的月和日,当月份为12,当前天数大于31那么就更新年份,每次要判断是闰年还是平年,以此更新2月份的天数

#include<bits/stdc++.h>
using namespace std;
int month[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int check(int x){
    if((x%4==0&&x%100!=0)||x%400==0)return 1;
    return 0;
}
 int a,b,c,n;
int main(){

   
               

    while(cin>>a>>b>>c>>n){
        
       for(int i = 1;i<=n;i++){
           if(check(a))month[2]=29;
           else month[2] = 28;
           c++;
           if(c>month[b]){//大于当前月的合法天数
               c = 1;
               b++;
               if(b>12){//如果月份大于12,则更新年份,月,日
                   b=1;
                   a++;
                   
               }
           }

       }
        printf("%02d-%02d-%02d\n",a,b,c);
    }


    return 0;
}

B.牌型种数

小明被劫持到X赌城,被迫与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序
自己手里能拿到的初始牌型组合一共有多少种呢?

思路:不考虑顺序,那么,每种牌都面临选和不选的选择,选的话,有选1,2,3,4的情况

那么直接dfs

#include<bits/stdc++.h>
using namespace std;
int n = 13;
int f[15];
int cnt;
void dfs(int dep,int sum){
   if(sum<0)return;
    if(dep>13){
        if(sum==0)
        cnt++;
        return;
    }
    dfs(dep+1,sum);

    dfs(dep+1,sum-1);
     
    dfs(dep+1,sum-2);

      
        dfs(dep+1,sum-3);
    
    
        dfs(dep+1,sum-4);
   
}
int main(){
    
    dfs(1,13);
    cout<<cnt<<endl;

    return 0;
}

C.垒骰子(dp思路,赶时间可以跳过这道题)

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

根据y氏dp法,

f[i][j] 表示前i个骰子且第i个骰子数字j朝上

状态转移 f[i][j] = f[i - 1][k] * 4 (侧面可旋转四次) (k从1到6 前i-1个骰子数字k朝上且k不与j的对面互斥)

#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 10, mod = 1e9 + 7;

int n, m;
int op[7] = { 0, 4, 5, 6, 1, 2, 3 };//反面 1 - 4, 2 - 5, 3 - 6

LL fast_power(int a, int k, int p)
{
    LL res = 1;
    while (k)
    {
        if (k & 1) res = res * a % p;
        a = a * (LL)a % p;//注意强制转换为long long
        k >>= 1;
    }
    return res;
}

//定义一个矩阵结构体
struct Matrix
{
    int row, col;//行数与列数
    LL a[N][N];//这道题目使用long long
    Matrix(int x, int y)
    {
        memset(a, 0, sizeof a);
        row = x, col = y;
    }
};

// 矩阵乘法 a * b mod p
Matrix mul(const Matrix & a, const Matrix & b, int p)
{
    Matrix ans(a.row, b.col);//答案的行数和a的行数相等,列数与b的列数相等
    for (int i = 1; i <= ans.row; i++)
        for (int j = 1; j <= ans.col; j++)
            for (int k = 1; k <= a.col; k++)
                ans.a[i][j] = (ans.a[i][j] + a.a[i][k] * b.a[k][j]) % p;

    return ans;
}

//矩阵的快速幂---a^k mod p
Matrix fast_power(Matrix a, int k, int p)
{
    Matrix ans(a.row, a.col);
    for (int i = 1; i <= a.row; i++) ans.a[i][i] = 1;//初始答案为单位矩阵
    while (k)
    {
        if (k & 1) ans = mul(ans, a, p);
        a = mul(a, a, p);
        k >>= 1;
    }
    return ans;
}

int main()
{
    scanf("%d%d", &n, &m);
    Matrix A(6, 6);
    for (int i = 1; i <= 6; i++)
        for (int j = 1; j <= 6; j++)
            A.a[i][j] = 1;

    for (int i = 0; i < m; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        A.a[op[a]][b] = A.a[op[b]][a] = 0;
    }

    //数据处理 
    Matrix ans = fast_power(A, n - 1, mod);

    LL sum = 0;
    for (int i = 1; i <= 6; i++)
        for (int j = 1; j <= 6; j++)
            sum = (sum + ans.a[i][j]) % mod;

    //结果输出 
    printf("%d\n", (sum * fast_power(4, n, mod)) % mod);

    return 0;
}


参考链接:第六届蓝桥杯【省赛试题9】垒骰子 ( 矩阵快速幂 )_i逆天耗子丶的博客-CSDN博客

奖券数目

有些人很迷信数字,比如带“4”的数字,认为和“死”谐音,就觉得不吉利。
虽然这些说法纯属无稽之谈,但有时还要迎合大众的需求。
某抽奖活动奖券号码是5位数(10000-99999),要求其中不要出现带“4”的号码。
主办单位请你计算一下,如果任何两张奖券不重号,最多可发出奖券多少张。

思路:数据较小,直接暴力meiju

将枚举的每个数转换为字符串,看其中是否包含4

#include<bits/stdc++.h>
using namespace std;
int main(){

int res =0 ;
    for(int i = 10000;i<=99999;i++){
        string s = to_string(i);
        if(s.find("4")==string::npos)res++;
    }
    cout<<res<<endl;
    return 0;
}

三羊献瑞

观察下面的加法算式:


其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。
请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。

思路:直接枚举所有不同数的排列,

上代码:
 

#include<bits/stdc++.h>
using namespace std;
int aa[10] = {0,1,2,3,4,5,6,7,8,9};
int main(){
    int sum1,sum2,sum3;
    do{
        if(aa[0]!=0&&aa[4]!=0){
			sum1=aa[4]*1000+aa[3]*100+aa[5]*10+aa[6];
			sum2=aa[0]*1000+aa[1]*100+aa[2]*10+aa[3];
			sum3=aa[0]*10000+aa[1]*1000+aa[5]*100+aa[3]*10+aa[7];
			if(sum3==(sum1+sum2)){
			break;
			}
    }
    
    }while(next_permutation(aa,aa+10));//全排列函数,不会赶紧学学

    cout<<aa[0]<<aa[1]<<aa[2]<<aa[3]<<endl;



}

 加法变乘法

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

思路:我们可以枚举 i 和 j (分别代表两个*号的位置)的位置,找到除了示例之外的答案

第一次找到就结束程序

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

int main(){
	
	int sum = 1225;
	//我们让i从2开始,代表*的位置,他表示(i*(i-1)),同理枚举j
	//每次我们找的时候要减去*号前后两个数,即共四个数,再加上*的结果 
	for(int i = 2;i+2<=49;i++){
		if(i==11)continue;
		for(int j = i+2;j<=49;j++){
			int t = sum-i-(i-1);
			t-=j-(j-1);
			t+=(i*(i-1))+(j*(j-1));
			if(t == 2015){
				cout<<i-1<<endl;
				return 0;
			}
		}
	}
	
	return 0;
} 

生命之树

在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, ..., vk, b} 
使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。
经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。
但是由于 atm 不擅长计算,他不知道怎样有效的求评分。
他需要你为他写一个程序来计算一棵树的分数。

思路:

要求求出S最大的值,S其实是一个联通块。所以就是求所有联通块里面值最大的那个。

树形dp
一般使用f[u]表示树形dp
本题中,f[u]表示的是以u为根节点的连通块的sum的最大值
f[u]=w[u]+max(子树的情况,0)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int e[maxn],ne[maxn],h[maxn],idx;
void add(int a,int b){
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
typedef long long ll;
int n;
ll f[maxn];
ll w[maxn];
void dfs(int u,int far){
	w[u] = f[u];
	ll t;
	for(int i = h[u];~i;i=ne[i]){
		int j = e[i];
		if(j==far)continue;
		dfs(j,u);
		
	w[u] += max(0ll,w[j]);
	}
}
int main(){
	memset(h,-1,sizeof h);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>f[i];
	
	for(int i=0;i<n-1;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}	
	dfs(1,-1);
	ll res = -0x3f3f3f3f;
	for(int i = 1;i<=n;i++)res = max(res,w[i]);
	
	cout<<res<<endl;
	return 0;
} 

剪邮票

如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)


比如,下面两张图中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

思路:我们可以发现,满足条件的是一个连通块,所以我们可以枚举选哪五个格子,然后找到一个选的点,去判断这个连通块个数是否为5个即可

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;

const int maxn = 10;
int g[5][5];
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int vis[maxn][maxn];
int ans= 0 ;
int a[13] = {0,0,0,0,0,0,0,1,1,1,1,1};
typedef pair<int,int>pii;
int bfs(int sx,int sy){
	memset(vis,0,sizeof vis);
		queue<pii>q;
		q.push({sx,sy});
		int cnt=1;
		vis[sx][sy]=1;
		while(q.size()){
			auto t = q.front();q.pop();
			for(int i=0;i<4;i++){
				int nx = t.x+dx[i];
				int ny = t.y+dy[i];
				if(nx<0||nx>=3||ny<0||ny>=4||vis[nx][ny])continue;
				if(!a[nx*4+ny])continue;
				vis[nx][ny]=1;
				cnt++;
				q.push({nx,ny});
			}
		}
		//cout<<cnt<<endl;
		return cnt==5;
}
int main(){
	
	
	do{
		for(int i = 0;i<=11;i++){
			if(a[i]){
				if(bfs(i/4,i%4))ans++;
				break;
			}
		}
		
	} while(next_permutation(a,a+12));

	cout<<ans<<endl;
	
	return 0;
}

 最大比例

X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。并且,相邻的两个级别间的比例是个固定值。
也就是说:所有级别的奖金数构成了一个等比数列。比如:16,24,36,54。其等比值为:3/2
现在,我们随机调查了一些获奖者的奖金数。请你据此推算可能的最大的等比值。 

输入存在多组测试数据
第一行为数字 N (0<N<100),表示接下的一行包含N个正整数
第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额

思路:

我们要找一个数列的最大的等比值。
这种感觉和等差数列很相似,我们直接往GCD上靠

由于一串数据,排序之后肯定存在等差q

s= s1, s2 , s3 …sq … sn
s= aq^0,aq^1, aq^2 …a^q^k....aq^n

每个位次上都可以转换为[p/q]^w形式,即si=[s/a0,s/ai]^k
存在公比形如[p/q]^k
因为[p/q]^k>0,要使得整体最大则公比系数最大,则求K最大

k的限制条件
[p/q]^w是[p/q]^k的次幂
[p/q]^w=([p/q]^k)^s=[p/q]^k^s
则k是每一个w的约数
k最大就是wi的最大公约数

状如[p/q]^wi
我们现在转向了求每一个wi的最大公约数

[p/q]^k = [p^k/q^k]
我们可以直接求取分子分母
则分别求分子分母指数最大公约数
 



#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 110;
ll a[maxn],b[maxn];
ll nums[maxn];
ll gcd(ll a,ll b){
    if(!b)return a;
    
    return gcd(b,a%b);
}
ll get_sub(ll a,ll b){
    
    if(a<b)swap(a,b);
    if(b==1)return a;
    return get_sub(b,a/b);
}
int main()
{
    int n;
    cin>>n;
    for(int i= 0;i<n;i++){
        cin>>nums[i];
        
    }
    sort(nums,nums+n);
    int cnt=0;
    for(int i=1;i<n;i++){//转换为分子,分母,(nums[i]/nums[0])(p/q)^k的形式
            if(nums[i]!=nums[i-1]){
           ll d = gcd(nums[i],nums[0]);
            a[cnt] = nums[i]/d; 
            b[cnt] = nums[0]/d;
            cnt++;
        }
    }
    
    ll up = a[0],down = b[0];
    for(int i=1;i<cnt;i++){
        up = get_sub(up,a[i]);
        down  = get_sub(down,b[i]);
    }
    cout<<up<<'/'<<down<<"\n";
    return 0;
}

密码脱落

X星球的考古学家发现了一批古代留下来的密码。这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
你的任务是:给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

y氏dp法

#include<iostream>
using namespace std;
string s;
const int maxn = 1100;
int f[maxn][maxn];
int main()
{
    cin>>s;
    int n = s.size();

    for(int len=1;len<=n;len++){
        
        for(int i=0;i+len-1<n;i++){
            
            int j = i+len-1;
            if(len==1) f[i][j]=1;
            else
            {
                f[i][j] = max(f[i][j-1],f[i+1][j]);
                
                if(s[i]==s[j]){
                    f[i][j] =max(f[i][j], f[i+1][j-1]+2);
                    
                }
            }
        }
    }
    cout<<n-f[0][n-1]<<endl;
    
    return 0;
}

方格分割 

6x6的方格,沿着格子的边线剪开成两部分。要求这两部分的形状完全相同。如图就是可行的分割法。 


试计算:包括这3种分法在内,一共有多少种不同的分割方法。注意:旋转对称的属于同一种分割法。 

方格分割 (dfs+思维)_风遥~的博客-CSDN博客

X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。

每块金属原料的外形、尺寸完全一致,但重量不同。
金属材料被严格地堆放成金字塔形。

                             7
                            5 8
                           7 8 8
                          9 2 7 2
                         8 1 4 9 1
                        8 1 8 8 4 1
                       7 9 6 1 4 5 4
                      5 6 5 5 6 9 5 6
                     5 5 4 7 9 3 5 5 1
                    7 5 7 9 7 4 7 3 3 1
                   4 6 4 5 5 8 8 3 2 4 3
                  1 1 3 3 1 6 6 5 5 4 4 2
                 9 9 9 2 1 9 1 9 2 9 5 7 9
                4 3 3 7 7 9 3 6 1 3 8 8 3 7
               3 6 8 1 5 3 9 5 8 3 8 1 8 3 3
              8 3 2 3 3 5 5 8 5 4 2 8 6 7 6 9
             8 1 8 1 8 4 6 2 2 1 7 9 4 2 3 3 4
            2 8 4 2 2 9 9 2 8 3 4 9 6 3 9 4 6 9
           7 9 7 4 9 7 6 6 2 8 9 4 1 8 1 7 2 1 6
          9 2 8 6 4 2 7 9 5 4 1 2 5 1 7 3 9 8 3 3
         5 2 1 6 7 9 3 2 8 9 5 5 6 6 6 2 1 8 7 9 9
        6 7 1 8 8 7 5 3 6 5 4 7 3 4 6 7 8 1 3 2 7 4
       2 2 6 3 5 3 4 9 2 4 5 7 6 6 3 2 7 2 4 8 5 5 4
      7 4 4 5 8 3 3 8 1 8 6 3 2 1 6 2 6 4 6 3 8 2 9 6
     1 2 4 1 3 3 5 3 4 9 6 3 8 6 5 9 1 5 3 2 6 8 8 5 3
    2 2 7 9 3 3 2 8 6 9 8 4 4 9 5 8 2 6 3 4 8 4 9 3 8 8
   7 7 7 9 7 5 2 7 9 2 5 1 9 2 6 5 3 9 3 5 7 3 5 4 2 8 9
  7 7 6 6 8 7 5 5 8 2 4 7 7 4 7 2 6 9 2 1 8 2 9 8 5 7 3 6
 5 9 4 5 5 7 5 5 6 3 5 3 9 5 8 9 5 4 1 2 6 1 4 3 5 3 2 4 1
X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X

其中的数字代表金属块的重量(计量单位较大)。
最下一层的X代表30台极高精度的电子秤。

假设每块原料的重量都十分精确地平均落在下方的两个金属块上,
最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。
电子秤的计量单位很小,所以显示的数字很大。

工作人员发现,其中读数最小的电子秤的示数为:2086458231

请你推算出:读数最大的电子秤的示数为多少?

注意:需要提交的是一个整数,不要填写任何多余的内容。

思路:从头到低叠加重量
 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

double data[50][30]= {0};
int main()
{
    for(int i=0; i<29; i++)
    {
        for(int j=0; j<=i; j++)
        {
            cin>>data[i][j];
        }
    }

    for(int i=0; i<30; i++)
    {
        for(int j=0; j<=i; j++)
        {
            data[i+1][j]=data[i][j]/2+data[i+1][j];
            data[i+1][j+1]=data[i][j]/2+data[i+1][j+1];
        }
    }
    sort(data[29],data[29]+29,greater<double>());
    printf("\n");
    for(int i =0;i<30;i++)
        cout<<data[29][i]<<" ";
    printf("\n%lf",data[29][0]/data[29][29]*2086458231);

    return 0;
}

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数  
2. 大小相同  
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

二分模板:
 

#include<iostream>
#define x first
#define y second
using namespace std;
const int maxn = 1e5+10;
typedef pair<int,int>pii;
pii a[maxn];
int n,k;
int f(int x){
	int cnt=0;
	for(int i=0;i<n;i++){
		int c = min(a[i].x,a[i].y);
		cnt+=(a[i].x/x)*(a[i].y/x);
	}
	return cnt>=k;
}
int main(){
	cin>>n>>k;
	int r=1;
	for(int i=0;i<n;i++){
		cin>>a[i].x>>a[i].y;
		r = max(r,a[i].x);
		r = max(a[i].y,r);
	}
//	r = 100000;
	int l = 1;
	while(l<r){
		int mid = l+r+1>>1;
		if(f(mid))l=mid;
		else r = mid-1;
	}
	cout<<l<<endl;
	return 0;
}

题目描述

给定一个长度为N的数列,A1, A2, ... AN。
如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。  
你能求出数列中总共有多少个K倍区间吗? 

思路:(求区间[l,r]的和是k的倍数的个数。求区间和,我们可以通过前缀和来求出。我们规定sum[i]表示第1个元素到第i个元素的和。那么sum[r] - sum[l-1]就是区间[l,r]的和。区间[l,r]的和是k的倍数即(sum[r] - sum[l-1])%k == 0 即sum[r]%k == sum[l-1]%k)

#include<iostream>
using namespace std;
typedef long long ll;
ll a[100010];
ll cnt[100010];
int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i] +=a[i-1];
    }
    cnt[0]=1;
    ll res = 0 ;
    for(int i = 1;i<=n;i++){
        res+=cnt[a[i]%k];
        cnt[a[i]%k]++;
    }
    
    cout<<res<<endl;
    
    
    return 0;
}

如图所示: 有9只盘子,排成1个圆圈。其中8只盘子内装着8只蚱蜢,有一个是空盘。


我们把这些蚱蜢顺时针编号为 1~8。每只蚱蜢都可以跳到相邻的空盘中,也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。
请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列,并且保持空盘的位置不变(也就是1-8换位,2-7换位,...),至少要经过多少次跳跃? 

本题通过map存储字符串判断是否进入队列,先找到空杯子的位置然后对能跳到空杯子的位置进行交换,具体可以看代码。因为只是个填空题所以通过string存储

#include<cstdio>
#include<algorithm> 
#include<iostream>
#include<string>
#include<map>
#include<queue>
using namespace std;
struct node{
	int step;
	string s;
};
int X[4] = {-2,-1,1,2};
string start = "*87654321";
string endd = "*12345678";
map<string,int> vis;
int BFS(){
	queue<node> q;
	node Node;
	Node.step = 0;
	Node.s = start;
	q.push(Node);
	while(!q.empty()){
		node front = q.front();
		q.pop();
		if(front.s == endd)
			return front.step;
		int pos = front.s.find("*");
		for(int i = 0 ; i < 4;i++){
			string temp = front.s;
			int nowpos = (pos + X[i]+9)%9;
			swap(temp[pos],temp[nowpos]);
			if(vis[temp] == 0){
				vis[temp] = 1;
				Node.s = temp;
				Node.step = front.step + 1;
				q.push(Node);
			}
		}
	}
}
int main(void){
	int ans = BFS();
	cout << ans;
	return 0;
}

迷宫

下图给出了一个迷宫的平面图,其中标记为1 的为障碍,标记为0 的为可
以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这
个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,
一共10 步。其中D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行50 列),请找出一种通过迷宫的方式,
其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中D<L<R<U。

#include <bits/stdc++.h>
using namespace std;
int vis[50][100];
int a[50][100];
string ss;
int minn=9999999;
int b[5]={0,1,0,0,-1},c[5]={0,0,-1,1,0};
char s[10];
int vv[2000];
int dp[50][100];
void dfs(int x,int y,int steps)
{
	if(steps>minn)	return;
	if(x==30&&y==50)
	{
		if(steps<minn)
		{
			minn=steps;
			string temp;
			for(int i=1;i<=steps-1;i++)
				temp+=s[vv[i]];
			ss=temp;	
		}
		return;
	}
	for(int i=1;i<=4;i++)
	{
		int nx=x+b[i],ny=y+c[i];
		if(nx<1||ny<1||nx>30||ny>50)	continue;
		if(a[nx][ny]==1||vis[nx][ny])	continue;
		if(steps+1>dp[nx][ny])		return;
		dp[nx][ny]=steps+1;
		vv[steps]=i;
		vis[nx][ny]=1;
		dfs(nx,ny,steps+1);
		vis[nx][ny]=0;
	}
}
int main()
{
	for(int i=0;i<=40;i++)
	{
		for(int j=1;j<=60;j++)
			dp[i][j]=9999999;//初始化 
	}
	for(int i=1;i<=30;i++)
	{
		for(int j=1;j<=50;j++)
		{
			a[i][j]=(getchar()-'0');
		}
		getchar();
	}
	s[1]='D',s[2]='L',s[3]='R',s[4]='U';
	vis[1][1]=1;//记得起点标记 
	dfs(1,1,1);
	cout<<ss;

}

bfs

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int x,y;
    string path;
};
bool vis[35][55];
char st[35][55];
int dx[]={1,0,0,-1};
int dy[]={0,-1,1,0};
char str[]={'D','L','R','U'};
int n=30,m=50;
queue<node>q;
void bfs()
{
    node now;
    now.x=1;now.y=1;now.path="";
    q.push(now);
    vis[now.x][now.y]=1;
    while(!q.empty())
    {
        node tmp=q.front();
        q.pop();
        if(tmp.x==n&&tmp.y==m)
        {
            cout<<tmp.path;
            return;
        }
        for(int i=0;i<4;i++)
        {
            node next;
            next.x=dx[i]+tmp.x;
            next.y=dy[i]+tmp.y;
            if(next.x>=1&&next.x<=30&&next.y>=1&&next.y<=50&&vis[next.x][next.y]==0)
            {
                next.path=tmp.path+str[i];
                vis[next.x][next.y]=1;
                q.push(next);
            }
        }
    }
}
int main()
{
    for(int i=1;i<=n;i++)cin>>st[i]+1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(st[i][j]=='0')vis[i][j]=0;
            else vis[i][j]=1;
        }
    }
    bfs();
    return 0;
}

七段码

小蓝要用七段码数码管来表示一种特殊的文字。

 


上图给出了七段码数码管的一个图示,数码管中一共有7 段可以发光的二极管,分别标记为a, b, c, d, e, f, g。
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?

并查集

#include"iostream"
#include"cstring"
using namespace std;
int fa[10];
bool vis[10];
// a -> 0    b -> 1   c -> 2
// d -> 3    e -> 4   f -> 5   g -> 6 
int ans;
int get(int cur){
	if(cur == fa[cur]) return cur;
	return get(fa[cur]);
}

void merge(int cx,int cy){
	int xx = get(cx);
	int yy = get(cy);
	
	fa[xx] = yy;
}
int main(){
	
	for(int st = 1;st < (1<<7);st ++){
		
		memset(vis,false,sizeof(vis));
		
		for(int i = 0;i <= 6;i ++) fa[i] = i;
		
		
		for(int i = 0;i <= 6;i ++){ //这里的从 0 开始计数也很巧,因为第一次是1左移0位 
			if(st & (1<<i)){
				vis[i] = true;
				if(i == 0){
					if(vis[1]) merge(0,1);
					if(vis[5]) merge(0,5);
				}
				
				if(i == 1){
					if(vis[0]) merge(1,0);
					if(vis[6]) merge(1,6);
					if(vis[2]) merge(1,2);
				}
				
				if(i == 2){
					if(vis[1]) merge(2,1);
					if(vis[3]) merge(2,3);
					if(vis[6]) merge(2,6);
				}
				
				if(i == 3){
					if(vis[2]) merge(3,2);
					if(vis[4]) merge(3,4);
				}
				
				if(i == 4){
					if(vis[3]) merge(4,3);
					if(vis[5]) merge(4,5);
					if(vis[6]) merge(4,6);
				}
				
				if(i == 5){
					if(vis[0]) merge(5,0);
					if(vis[4]) merge(5,4);
					if(vis[6]) merge(5,6);
				}
				
				if(i == 6){
					if(vis[1]) merge(6,1);
					if(vis[2]) merge(6,2);
					if(vis[4]) merge(6,4);
					if(vis[5]) merge(6,5);
				}
			}
			
			
		}
		// 判断是否是一个连通块
		int cnt = 0;
		for(int i = 0;i <= 6;i ++){
			if(vis[i] && (i == fa[i])){
				cnt ++;
			}
		}
		
		if(cnt == 1) ans ++; 
	}
	
	cout << ans;
	return 0;
}

平面切分

平面上有N 条直线,其中第i 条直线是y = Ai * x + Bi。
请计算这些直线将平面分成了几个部分。


————————————————
版权声明:本文为CSDN博主「印象之外ost」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44564818/article/details/109296327

你有一架天平和N 个砝码,这N 个砝码重量依次是W1, W2, ... , WN。
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。

#include<iostream>
using namespace std;
const int maxn = 1e5+10;
int res =0 ;
int n;
int a[maxn];
int w[maxn];
void dfs(int s,int sum){
    if(s==n){
        if(sum>0&&!a[sum]){res++;
        
            a[sum]=1;
        }
        return;
    }
    dfs(s+1,sum);
    dfs(s+1,sum-w[s]);
    dfs(s+1,sum+w[s]);
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>w[i];
    dfs(0,0);
    cout<<res<<endl;
    
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int f[200][maxn];
int w[maxn];
int sum=0;
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i],sum+=w[i];
    int res =0 ;
    f[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=sum;j++){
            if(f[i-1][j]||f[i-1][abs(j-w[i])]||f[i-1][j+w[i]]){
                f[i][j]=1;
            }
        }
    }
    for(int i=1;i<=sum;i++){
        if(f[n][i])res++;
    }
    
    cout<<res<<endl;
    return 0;
}

持续更新中.....

(点个赞吧!)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值