ALGO-1007 印章(DP)
#include<iostream> #include<iomanip> #include<math.h> using namespace std; double dp[22];//一维数组,dp[i]:买i张印章,凑齐n种印章的概率 /*为什么用一维数组? 因为在此解法所用的递推公式中, 买m张印章,凑齐n种印章的概率只与买m张印章,凑齐n-1种,n-2种...1种 的概率有关 , 与买x(x!=m)张印章凑齐y(y>=1)种印章的概率无关。 这是一个具体的问题,就是说明了买了m张,求的是里面刚好有n,这个事件的概率*/ long long c_fun(int n, int m)//求C(n,m),注意n为下标,m为上标 { long i; long long ans_n = 1, ans_m = 1, ans_nm = 1; for (i = 2; i <= n; i++) ans_n = ans_n * i; for (i = 2; i <= m; i++) ans_m = ans_m * i; for (i = 2; i <=(n-m) ; i++) ans_nm = ans_nm * i; return ans_n / (ans_m * ans_nm); } double fun(int n, int m)//dp[n],求印章问题,待求事件的概率 { int i, j; dp[1] = 1;//对于凑齐1种,只要买了概率的发生一定是1。 for (i = 2; i <= n; i++)//这个循环求凑齐2、3、...n种的概率,后面一个数字发生的概率需要前一个发生的概率 { double t = 0; //i代表凑齐的种数,j的取值为1--m, for (j = 1; j < i; j++)//当j>i时,意味着买的印章数<凑齐的印章种数,此事件不可能发生 t += c_fun(i, j) * pow(j, m) * dp[j];//求逆事件的概率 dp[i] = 1 - t / pow(i, m);//总概率为1,1减去逆事件的概率为待求事件概率; //看到这一步,其实可以想到另一种方法直接求待求事件的概率; } return dp[n]; } int main() { int n, m;//n=凑齐的种数,m=买的印章数; cin >> n >> m; dp[n] = fun(n, m); cout << fixed<<setprecision(4)<< dp[n] << endl; return 0; }
做这道题发现蓝桥杯系统有bug诶,就算是通过了所有的测试,代码的逻辑还是有问题的。不过这也是很正常的是,测试代码是否正确他只看输入输出是否正确,只要通过那几组测试,都没啥大问题。
参考文章: 蓝桥杯算法训练 印章_!YI的博客-CSDN博客
方法二:
#include<iostream> #include<math.h> #include<iomanip> using namespace std; double dp[25][25] ; int main() { int n, m; cin >> n >> m; double p=1.0/n;//每种出现的概率,设一个double型的p也为方面了后面的运算,使运算结果为double型 for (int i = 1; i <= m; i++) { dp[i][1] = pow(p, i - 1);//dp[i][1]=p^(n-1); for (int j = 2; j <= i; j++) dp[i][j] = dp[i-1][j - 1] * ((n-j+1)*p) + dp[i - 1][j]*(j*p); //((n-j+1)/n) 典型错误,计算结果为0 /*我们买的第 i 张,有两种状态,一:跟前面买的 i - 1 张中有重复的 二:跟前面买的 i - 1 张中没有重复的 买第i张如果有重复的着事件发生的概率为dp[i - 1][j]*(j*p); 如果没有重复的则抽到第i与前面的种数有重复则事件发生的概率为dp[i-1][j - 1] * ((n-j+1)*p) 注意是dp[i-1][j - 1] * ((n-j+1)*p)不是dp[i][j - 1] * ((n-j+1)*p),抽一张新的种类i、j都要+1, 所以对于dp[i][j]的概率,是上面两种可能概率相加*/ } cout << fixed<<setprecision(4)<<dp[m][n] << endl; return 0; }
其实这个方法可以设数组为一维数组,还有想说的是for循环yyds!!!
贴一下下面分享的博主写的,他的for里面加了if语句,这个想法很好诶!
#include <iostream> #include <cmath> using namespace std; double dp[25][25], p; int main() { //记住是小数啊,要*1.0进行类型转换的 int n, m; cin >> n >> m; p = 1.0 / n; //每种出现的概率 for ( int i = 1; i <= m; ++i ) { for ( int j = 1; j <= n; ++j ) { if ( i < j ) dp[i][j] = 0; if ( j == 1 ) { dp[i][j] = pow (p, i-1); //p^(i-1) } else { dp[i][j] = dp[i-1][j] * (j*1.0/n) + dp[i-1][j-1] * ((n-j+1)*1.0/n); } } } // cout << "dp\n"; // for ( int i = 1; i <= m; ++i ) { // for ( int j = 1; j <= n; ++j ) { // printf("%.2lf ",dp[i][j]); // } // cout << endl; // } printf("%.4lf ",dp[m][n]); return 0; }
总结一下这个题考的就是数学,求概率,可惜我概率论学的撇啊!
参考文章: 蓝桥杯 试题 算法训练 印章_okok__TXF的博客-CSDN博客
ALGO-1006 拿金币(DP)
以下n行描述该方格。金币数保证是不超过1000的正整数。
1 3 3
2 2 2
3 1 2
#include<iostream> using namespace std; int dp[1002][1002] = { 0 }; int main() { int n; cin >> n; for (int i = 1; i <= n; i++)//给一个位置赋值,值代表这个位置金币的数量 for (int j = 1; j <= n; j++) cin >> dp[i][j]; for (int i = 1; i <= n; i++)//遍历所有的位置,每一个位置的值都+=max(左边或者上边) for (int j = 1; j <= n; j++)//在草稿纸上面画一哈,其实就可以确定走的路径, { //对于dp[n][n]的值来说,它一定的走的所有路径中所加值最大的那条路的值 if (dp[i - 1][j] > dp[i][j - 1]) dp[i][j] += dp[i - 1][j]; else dp[i][j] += dp[i][j - 1]; } cout << dp[n][n] << endl;//分析可知要使捡的金币最多一定是走到了(n,n)这个位置的。 return 0; }
参考文章: LanQiao-ALGO-1006 拿金币(动态规划) (类似 LeetCode 62.不同路径)_Mr.xiao的博客-CSDN博客
感谢大佬的文章,这个题其实考了图的知识。
ALGO-1005 数字游戏(搜索)
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
#include<iostream> #include<vector> #include<algorithm> using namespace std; int c_fun(int a, int b)//求C(a,b) { int ans_a = 1, ans_b = 1, ans_ab = 1; for (int i = 2; i <= a; i++) ans_a *= i; for (int i = 2; i <= b; i++) ans_b *= i; for (int i = 2; i <= a - b; i++) ans_ab *= i; return ans_a / (ans_b * ans_ab); } int main() { int n, sum; cin >> n >> sum; vector<int> lst; for (int i = 1; i <= n; i++) lst.push_back(i); do { int temp = 0; for (int i = 0; i < n; i++) temp += lst[i] * c_fun(n - 1, i);//规律,正推推出来的 temp = a[0]*C(0,n-1) + a[1]*C(1,n-1) + ... +a[n-1]*C(n-1,n-1); if (sum == temp)//在if里面写输出语句保证了输出结果存在则有输出,不存在无输出 { for (vector<int>::iterator it = lst.begin(); it != lst.end(); it++) cout << *it << " "; break; } } while (next_permutation(lst.begin(), lst.end())); return 0; }
参考文章: 蓝桥杯 数字游戏 C++_m0_56318237的博客-CSDN博客
方法二:dfs
dfs方法不是很好,下次来补充
ALGO-1004 无聊的逗
1 2 3 1
#include<iostream> #include<vector> #include<numeric> #include<algorithm> using namespace std; int maxLen = 0; void dfs (vector<int>dp, int n, int add, int compare = 0, int scan = 0) { if (scan >= n) return;//递归结束条件,遍历完所有可能的结果 //如果add==compare,且新计算得到的compare大于原来的maxLen,则更新maxLen; if (add == compare && maxLen < compare) maxLen = compare; //如果选择scan这个位置的木棍来组成compare的值 dfs(dp, n, add - dp[scan], compare + dp[scan], scan + 1); //如果不选则scan这个位置的木棍来组成compare的值 dfs(dp, n, add, compare, scan + 1); } int main() { int n, a = 0; cin >> n; vector<int>dp(n); for (int i = 0; i < n; i++) cin >> dp[i]; int sum = accumulate(dp.begin(), dp.end(), 0);//计算所有木棍的总长度 sort(dp.begin(), dp.end(), greater<int>());//逆序排 /*逆序排的好处?dp[0]存的为最大的值,方便后序处理。*/ /*此题的输入的棍子长度总数大概有三种情况, 1.max==sum/2; 2.max>sum/2,删除最大值后再排,也就是扫描的位置可以直接从1开始。 3.sum%2!=0,删除最小奇数 4.sum为偶数; 如果数能被分为两堆一样长,则这个数一定为偶数, 所以我们要把范围局限为偶数,偶数都没法,这没法分了*/ if ((double)dp[0] == sum / 2.0)//1. { cout << dp[0]; return 0; } //if 最大长度大于sum/2,则删除最大长度木棍; if (dp[0] > sum / 2)//2. dfs(dp, n, sum - dp[0], a, 1); else if (sum % 2 != 0)//3. { for (int i = n - 1; i >= 0; i--) if (dp[i] % 2 != 0) { sum = sum - dp[i]; for (int j = i; j < n - 1; j++) { dp[j] = dp[j + 1]; } n = n - 1; break; } dfs(dp, n, sum, a, 0); } else { dfs(dp, n, sum, a, 0);//4. } cout << maxLen << endl; return 0; }
参考:蓝桥杯 试题 算法训练 无聊的逗 C++ 详解_Lyz_ID的博客-CSDN博客
方法二.01背包
链接放下面,属于是没看懂,等今后再来看。
参考文章:LanQiao-ALGO-1004 无聊的逗 (动态规划: 0-1背包问题) -- (LeetCode 416. 分割等和子集 变种题)_Mr.xiao的博客-CSDN博客
ALGO-1003 礼物
在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
这些石子很漂亮,JiaoShou决定以此为礼物。
但是这N个石子被施加了一种特殊的魔法。
如果要取走石子,必须按照以下的规则去取。
每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
由于时间紧迫,Jiaoshou只能取一次。
现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。
第二行N个整数,用空格隔开,表示每个石子的重量。
1 1 1 1 1 1 1 1
对于70%的数据:N<=100,000
对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负
#include<iostream> using namespace std; int n; long long s; const int M = 1e6 + 10;//包括了所有数组取值大小 int weight[M];//每一个石头的重量 long long suffix[M];//前i个石头的重量和 bool check(int mid) {//mid的值是数组的下标,也表示了输出石头的个数 for (int i = mid; i <= n - mid; i++) { if (suffix[i] - suffix[i - mid] <= s && suffix[i + mid] - suffix[i] <= s) //if 语句里面保证了前mid 个数小于s,后mid个数小于s ; return true; } return false; } int main() { cin >> n >> s; suffix[0] = 0;//给0位置赋值 0 for (int i = 1; i <= n; i++) { cin >> weight[i]; suffix[i] = suffix[i - 1] + weight[i]; } int l, r, mid; l = 1; r = n; while (l <= r) {//二分法 mid = (l + r) / 2; if (check(mid)) l = mid+1; else r = mid - 1; } cout << 2 * (l-1) << endl;//为什么l要-1呢?因为运行到l=r时肯定满足条件, //此时循环继续l的值变为l+1>r,调出循环,此时没有进行cheak()运算。 return 0; }
ALGO-1002 跳马
#include<iostream> #include<queue> using namespace std; bool vis[9][9] = { 0 };//建立9*9的数组,其中(1-8)*(1-8)代表棋盘 //dx和dy一一对应,表示马走一步代表的走法,共八种(0,0)不算 int dx[9] = { 0,-2,-2,-1,-1,1,1,2,2 }; int dy[9] = { 0,-1,1,-2,2,-2,2,-1,1 }; queue<node> q;//建立队列q,用BFS肯定需要队列辅助呀 /*建立一个节点,节点代表每一个点的坐标(x,y) 以及从初始位置走到此点需要走几步*/ struct node { int x, y; int step; node() :x(), y(), step() {}; node(int a, int b, int c) :x(a), y(b), step(c) {}; }; int BFS_search(int a, int b, int c, int d) { if (a == c && b == d)//如果目标点就是起始点 return 0; vis[a][b] = true;//代表此点已经走过,修改值为true q.push(node(a, b, 0));//进入队列 while (!q.empty()) { node t = q.front();//头指针的值赋值给t; q.pop();//出队 for (int i = 1; i <= 8; i++)//广度遍历关键操作,把每一种走法走一遍 { int cur_x = t.x + dx[i]; int cur_y = t.y + dy[i]; if (cur_x == c && cur_y == d) { return t.step + 1; } else if(cur_x>=1&&cur_x<=8&&cur_y>=1&&cur_y<=8&&!vis[cur_x][cur_y]) { q.push(node(cur_x, cur_y, t.step + 1)); vis[cur_x][cur_y] = true; } } } return -1; } int main() { int a, b, c, d; cin >> a >> b >> c >> d; cout << BFS_search(a, b, c, d); return 0; }
参考:试题 算法训练 跳马_Solar_angel的博客-CSDN博客
ALGO-1001 kAc给糖果你吃(贪心)
kAc说你只能拿m次糖果,聪明的你当然想要拿最多的糖果来吃啦啦啦~
//第二天,kAc问你还想吃糖果么?(嘿嘿嘿)说着眼角路出奇怪的微笑...
1 2
其余数字都是不超过1,000,000,000的非负整数。
#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { int n, m; long long sum=0; cin >> n >> m; vector<long long> A(n); for (int i = 0; i < A.size(); i++) cin>> A[i]; sort(A.begin(), A.end(), greater<long long>());//从大到小排 for (int i = m,j=0; i > 0; i--,j++)//取的每一个数都是能取数中,最大的(贪心) { sum += A[j]; } cout << sum; return 0; }
这是我做过的算法题中最简单的,没有之一!
ALGO-999 数的潜能
给定N,求它的潜能M。
由于M可能过大,只需求M对5218取模的余数。
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; #define MOD 5218 int qpow(int a, ll n)//递归实现快速模平方法 { if (n == 0) return 1; else if (n % 2 == 1) return qpow(a, n - 1) * a % MOD; else { int temp = qpow(a, n / 2); return temp * temp % MOD; } } int main() { ll n, h, m; int result; cin >> n; h = n / 3;//存有好多个3 m = n % 3;//余数 if (m == 1 && h > 0) { m = 4; h = h - 1; } if (m != 0) result = qpow(3, h) * m % MOD; else result = qpow(3, h); cout <<result<< endl; }
ALGO-998 娜神平衡
她暗恋的琦琦是一名学霸,他只喜欢长得漂亮和学习很好的女生。
娜娜学习确实很神,但是她在琦琦面前却总是表现不出平时的神力。
琦琦感受到了娜娜对他的爱,但是他还是觉得娜娜的学习并不是特别好,于是他出了一道题给娜娜。
“娜娜,我们之间的关系需要在不断深入的同时保持一定的平衡,不可以你总是强势或者我总是弱势。”
琦琦给了娜娜一些两两不等的数,希望娜娜能把这些数分成两组A和B,满足以下条件:
1:每一次只能操作一个数,即只取出一个数分入A中或B中;
2:每一次操作完成后,A中数之和与B中数之和的差不能超过r。
新时代的丘比特们啊,帮帮娜娜吧!
第一行包括两个正整数n和r,n表示琦琦一共给了n个数,r的意义见题目描述。
第二行包括n个正整数,分别表示琦琦给的n个数。
注意输入中n个数的第一个必须分入A组。
琦琦保证这样的输出唯一。
9 6 4 20
20
- 排序数据,便于枚举判断
- 使用队列存储数据,枚举失败的数据可以放回队尾,下次再枚举
- 使用栈存储枚举成功的顺序,便于回溯(正因为把顺序保存了下来,免于用递归)
- 题目要求第一个数只能放在A中,因此,只要发现B中存在第一个数,就把B当成A就行了(由答案的唯一性,可以知道一个组中的数永远待在一起,因此只要把存有第一个数的组当成A就可以了,没必要纠结于优先枚举进哪个数组)
#include<iostream> #include<algorithm> #include<queue> #include<vector> using namespace std; int main() { //sum1存lst1所有元素的和,sum2同理; int n, r,sum1=0,sum2=0; cin >> n >> r; vector<int>lst(n),lst1,lst2,per; /*per数组存的元素含义:1表示lst元素存进lst1, 2表示lst元素存进了lst2;*/ for (int i = 0; i < lst.size(); i++) { cin >> lst[i]; } int first = lst[0];//题目要求输入的第一个数要存进输出的第一个数组 sort(lst.begin(), lst.end());//从小到大 queue<int> q;//建立队列 for (int i = 0; i < lst.size(); i++) { q.push(lst[i]);//进入队列 } int t=0; while (!q.empty()) { t = q.front(); lst1.push_back(t);//进入lst1; sum1 += t; per.push_back(1); q.pop(); if ((sum1 - sum2) > r)//如果数进入lst1后,sum1-sum2>10 { lst1.pop_back();//出队 per.pop_back(); sum1 -= t; lst2.push_back(t);//入队 per.push_back(2); sum2 += t; } if ( sum2 - sum1 > r)//入队到lst2后,如果还是不满足条件 { q.push(t); lst2.pop_back();//出队 sum2 -= t; per.pop_back(); if (per.back() == 1)//如果上一次入队到lst1; { per.pop_back(); int m=lst1.back();//回溯 lst1.pop_back(); q.push(m);//再入队 sum1 -= m; } else //如果上一次入队到lst1; { per.pop_back(); int m = lst2.back(); lst2.pop_back();//回溯 q.push(m);//再入队 sum2 -= m; } } } sort(lst1.begin(), lst1.end());//排序 sort(lst2.begin(), lst2.end());//排序 for (int i = 0; i < lst2.size(); i++)//判断first是否再lst2里面 { if (first == lst2[i]) { swap(lst1, lst2);//如果在,则交换数组 break; } } for (int i = 0; i < lst1.size(); i++) cout << lst1[i] << " "; cout << endl; for (int i = 0; i < lst2.size(); i++) cout << lst2[i] << " "; return 0; }
ALGO-997 粘木棍
一行N个整数,表示木棍的长度。
10 20 40
#include<iostream> #include<algorithm> using namespace std; int temp, sum[9], flag[9] = { 0 }; /*temp 最大值和最小值的最大差距, sum[i] 第i组木棍总长度 falg 标准数组,判断木棍是否被分配到某一组 */ int lst[9], n, m; int pos;//遍历lst数组的当前位置 void DFS(int pos) { if (pos > n) { int s1 = sum[1],s2 = sum[1]; for (int i = 1; i <= m; i++) { s1 = min(s1, sum[i]); s2 = max(s2, sum[i]); } temp = min(s2 - s1, temp); } for ( int t = pos; t <= n; t++) { for (int j = 1; j <= m; j++) { if (flag[t]) break; flag[t] = 1; sum[j] += lst[t]; DFS(pos + 1); //回溯 flag[t] = 0; sum[j] -= lst[t]; } } } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> lst[i]; sort(lst, lst + n+1);//排序的目的只是找出最大值和最小值而已,不排也可 temp = lst[n] - lst[1];//初始temp的值为lst数组中,最大值-最小值 DFS(1); cout << temp; return 0; }
ALGO-996 车的放置
【样例解释】一个车都不放为1种,放置一个车有4种,放置2个车有2种。
#include<iostream> using namespace std; int ans = 1;//输出结果,好多种方法 int n; bool flag[10] = { 0 };//标志符号,0表示第i列没放小车 void DFS(int xx)//深度遍历 { if (xx > n)//递归,就一定得有一个跳出递归的条件 return; for (int i =1; i <= n; i++) { if (!flag[i])//如果第i列没放 { flag[i] = true; ans++;//为什么放一辆小车就+1呢? //放了一辆小车就改变了这个方阵上一次的状态,就是一种放法 DFS(xx + 1);//下一列 flag[i] = false;//回溯 } } DFS(xx + 1);//然后又从第二列、第三列开始放 } int main() { cin >> n; DFS(1);//从第一列开始放 cout << ans; return 0; }
ALGO-995 24点
((A*K)-J)*Q等价于((1*13)-11)*12=24
加减乘不用多说了,但除法必须满足能整除才能除!这样有一些是得不到24点的,所以这里只要求求出不超过24的最大值。
3
3
3
3
1
1
1
1
12
5
13
1
4
21