目录
1.印章(动态规划)
2.拿金币(动态规划)
3.数字游戏(搜索)
4.无聊的逗(状态搜索)
5.礼物(二分法和前缀和)
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
一行两个正整数n和m
输出格式
一个实数P表示答案,保留4位小数。
样例输入
2 3
样例输出
0.7500
数据规模与约定
1≤n,m≤20
示例代码
#include
#include
#include
using namespace std;
int main() {int n, m;float dp[21][21], p, temp = 0;//dp[i][j]为抽i个印章能凑齐j种印章的几率cin >> n >> m;p = float(1) / n;//只抽一个印章但抽到某一个特定印章的几率if (m < n) {//抽的印章数小于印章种类数,则这种几率为0cout << setprecision(4) << fixed << temp << endl;return 0;}dp[1][1] = 1;//只抽一个印章肯定能凑齐一种印章for (int i = 2; i <= m; i++) {//抽i个印章凑齐1种印章的几率,保证后面的i-1个印章抽的和第一个一样dp[i][1] = dp[i - 1][1] * p;}for (int j = 2; j <= m; j++) {for (int i = 2; i <= j; i++) {//抽m个印章凑齐i种印章的几率if (j == i) {//如果要抽的印章数和凑齐的印章数相等,则抽到的每一个印章都和之前的不一样dp[j][i] = dp[j - 1][i - 1] * ((n + 1 - i)) * p;}else {//否则可能是 抽到的最后一个印章和之前抽到的任意一个印章相同 以及 抽到的最后一个印章和之前的任意一个印章都不相同dp[j][i] = dp[j - 1][i] * i * p + dp[j - 1][i - 1] * (n + 1 - i) * p;}}}cout << setprecision(4) << fixed << dp[m][n] << endl;return 0;
}
问题描述
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
输入格式
第一行输入一个正整数n。
以下n行描述该方格。金币数保证是不超过1000的正整数。
输出格式
最多能拿金币数量。
样例输入
3
1 3 3
2 2 2
3 1 2
样例输出
11
数据规模与约定
n<=1000
示例代码
#include
#include
#include
int dp[1001][1001];
using namespace std;
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 = 2; i <= n; i++) {//对第一行进行处理,只能向右走dp[i][1] += dp[i - 1][1];}for (int i = 2; i <= n; i++) {//对第一列进行处理,只能向下走dp[1][i] += dp[1][i - 1];}for (int i = 2; i <= n; i++) {for (int j = 2; j <= n; j++) {dp[i][j] += max(dp[i - 1][j], dp[i][j - 1]);//选取向下和向右走的最大值,这样局部的dp[i][j]就是最大值}}cout << dp[n][n];return 0;
}
问题描述
给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
输入格式
第1行为两个正整数n,sum
输出格式
一个1~N的一个排列
样例输入
4 16
样例输出
3 1 2 4
数据规模与约定
0
示例代码
#include
#include
using namespace std;
int main() {int n, a[11][11], b[11], sum, sum1 = 0, count = 1, count1 = 1;cin >> n;cin >> sum;for (int i = n; i >= 1; i--) {count1 *= i;}for (int i = 1; i <= n; i++) {b[i] = i;}if (n == 1) {//求每一项的系数,类似于(a+b)的n次方的各项系数,和杨辉三角形很像a[1][1] = 1;}/*n 各项系数1 12 1 13 1 2 14 1 3 3 15 1 4 6 4 1...............*/else if (n == 2) {a[2][1] = 1;a[2][2] = 1;}else {for (int i = 2; i <= n; i++) {for (int j = 1; j <= i; j++) {if (j == 1 || j == i) {a[i][j] = 1;}else {a[i][j] = a[i - 1][j - 1] + a[i - 1][j];}}}}while (sum1 != sum) {//当还未找到需要的数时sum1 = 0;//sum1为此次1-n数字的排序的相邻总和for (int i = 1; i <= n; i++) {sum1 += a[n][i] * b[i];}count++;if (count == count1) {//如果1-n之间的数字排序排完了还是没有找到,说明不存在对应的结果return 0;}next_permutation(b + 1, b + n + 1);//将1-n组成的数进行从小到大的排序}prev_permutation(b + 1, b + n + 1);//多做了一次next则要prev降序一次for (int i = 1; i <= n; i++) {cout << b[i] << " ";}return 0;
}
问题描述
逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。
输入格式
第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。
输出格式
一个数,最大的长度。
样例输入
4
1 2 3 1
样例输出
3
数据规模与约定
n<=15
示例代码
#include
using namespace std;
int a[15];
int b[1 << 15] = { 0 };
int max(int a, int b) {if (a > b) {return a;}else {return b;}
}
int main() {int n, ans = 0;cin >> n;for (int i = 0; i < n; i++) {cin >> a[i];}for (int i = 0; i < (1 << n); i++) {//按照二进制来表示木棍选取情况 如1100表示选取的是第四根和第三根木棍,共2的n次方种选取情况for (int j = 0; j < n; j++) {if (i & (1 << j)) {//如果i放置的木棍中有第j根木棍 用与是因为二进制b[i] += a[j];}}}for (int i = 0; i < (1 << n); i++) {//先选取的木棍for (int j = 0; j < (1 << n); j++) {//后选取的木棍if (!(i & j) && b[i] == b[j]) {//如果不是同一种选取方式而且长度相同ans = max(ans, b[i]);}}}cout << ans;
}
问题描述
JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友。
在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
这些石子很漂亮,JiaoShou决定以此为礼物。
但是这N个石子被施加了一种特殊的魔法。
如果要取走石子,必须按照以下的规则去取。
每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
由于时间紧迫,Jiaoshou只能取一次。
现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。
输入描述
第一行两个整数N、S。
第二行N个整数,用空格隔开,表示每个石子的重量。
输出格式
第一行输出一个数表示JiaoShou最多能取走多少个石子。
样例输入
8 3
1 1 1 1 1 1 1 1
样例输出
6
样例解释
任意选择连续的6个1即可。
数据规模与约定
对于20%的数据:N<=1000
对于70%的数据:N<=100,000
对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负
示例代码
#include
#include
using namespace std;
int n;
long long s;
long long a[1000001], b[1000002] = { 0 };
bool inject(int c) {for (int i = c; i <= n - c; i++) {//i=c保证找到向左能有连续c个长度的石头,i<=n-c保证向右有连续c个长度的石头if (b[i] - b[i-c] <= s && b[c + i] - b[i] <= s) {//如果i左边的连续c个和右边连续c个石头的重量和都小于等于sreturn true;}}return false;//如果这个长度下都不符合
}
int main() {cin >> n >> s;for (int i = 1; i <= n; i++) {cin >> a[i];b[i] = b[i - 1] + a[i];//前缀和 b[n]-b[n-i]为 a[n-i+1]到a[n]之间的连续的和}int l = 1;int r = n;int k;while (l < r) {//当未找到时,l> 1;//二分法,取一半,(l+r)/2会造成死循环。。。。。。。。if (inject(k)) {//这个长度符合要求,可能有更大的长度l = k;}else {//这个长度不符合要求,长度要更小一些r = k - 1;}}cout <<2* l;
}