目录
11、递归与分支
1)递归
1.n 的阶乘(清华大学复试上机题)
题目描述:
输入一个整数 n,输出 n 的阶乘(每组测试用例可能包含多组数据,请注意处理)。
#include <bits/stdc++.h>
using namespace std;
long long factorial(long long n){
if(n == 0 || n == 1) return 1;
else if(n == 2) return 2;
else return n * factorial(n-1);
}
int main(){
long long n;
while(cin>>n){
cout<<factorial(n)<<endl;
}
return 0;
}
2.汉诺塔 III
题目描述:
约 19 世纪末,在欧洲的商店中出售一种智力玩具:在一块铜板上有三根杆,最左边的杆自上而下、由小到大顺序串着由 64 个圆盘构成的塔。目的是将最左边杆上的圆盘全部移到右边的杆上,条件是一次只能移动一个圆盘,并且不允许大圆盘放在小圆盘的上面。现在我们改变这个游戏的玩法:不允许直接从最左(右)边移动到最右(左)边(每次移动一定是移到中间杆或从中间杆移出),也不允许大圆盘放到小圆盘的上面。Daisy 已经做过原来的汉诺塔问题和汉诺塔II 问题,但碰到这个问题时,她想了很久都无法解决。请你帮助她。现在有N 个圆盘,她至少需要多少次移动才能把这些圆盘从最左边移到最右边?
#include <bits/stdc++.h>
using namespace std;
long long hanoi(long long n){
if(n == 1) return 2;
return 3 * hanoi(n-1) + 2;
}
int main(){
long long n;
while(cin>>n){
cout<<hanoi(n)<<endl;
}
return 0;
}
3.全排列(北京大学复试上机题)
题目描述:
给定一个由不同小写字母组成的字符串,输出这个字符串的所有全排列。假设对于小写字母有'a'<'b'<…<'y'<'z',而且给定的字符串中的字母已经按照从小到大的顺序排列。
#include <bits/stdc++.h>
using namespace std;
int main(){
string str;
while(cin>>str){
//直接调用库函数
do{
cout<<str<<endl;
}while(next_permutation(str.begin(),str.end()));
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
/*
递归:
abc的全排列 = a开头bc的全排列 + b开头ac的全排列 + c开头ab的全排列
其中,bc的全排列 = b开头c的全排列 + c开头b的全排列
以此类推......
*/
void fullPrint(string pre,string str){
if(str.size() == 1){
cout<<pre + str<<endl;
return;
}
string nextpre = "";
string nextstr = "";
for(int i = 0;i < str.size();i++){
nextpre = pre + str[i];
nextstr = str;
//删除下标为i的一个元素
nextstr.erase(i,1);
fullPrint(nextpre,nextstr);
}
}
int main(){
string str;
cin>>str;
fullPrint("",str);
return 0;
}
2)分治
1.二叉树(北京大学复试上机题)
题目描述:
如上所示,由正整数 1,2,3,…组成了一棵特殊二叉树。我们已知这棵二叉树的最后一个结点是n。现在的问题是,结点 m 所在的子树中一共包括多少个结点。比如,n = 12,m = 3,那么上图中的结点 13, 14, 15 及后面的结点都是不存在的,结点 m 所在子树中包括的结点有3, 6, 7, 12,因此结点 m 所在的子树中共有 4 个结点。
#include <bits/stdc++.h>
using namespace std;
int complete(int m,int n){
if(m == n) return 1;
else if(m < n) return 1 + complete(m*2,n) + complete(m*2+1,n);
else return 0;
}
int main(){
int m,n;
cin>>m>>n;
cout<<complete(m,n);
return 0;
}
12、搜索
1)BFS
1.Catch That Cow
题目描述:
农夫约翰被告知逃亡奶牛所在的位置,并希望能够立即抓住奶牛。他刚开始站在点N(0 ≤ N ≤ 100000)上,并且母牛站在同一条线上的点 K(0 ≤ K ≤ 100000)上。农夫约翰有两种交通方式:步行和传送。
行走:农夫约翰可以在 1 分钟内从任何一点 X 移动到点 X − 1 或点X + 1。
传送:农夫约翰可以在 1 分钟内从任何一点 X 移动到 2X 点。如果母牛不知道有人要去追它,根本不动,那么农夫约翰需要多长时间才能找回它?
#include <bits/stdc++.h>
using namespace std;
const int MAX = 100000;
bool visit[MAX];
struct states{
int local;
int time;
};
void BFS(int n,int t,int k){
queue<states> q;
memset(visit,false,sizeof(visit));
states s;
s.local = n;
s.time = 0;
q.push(s);
visit[n] = true;
while(!q.empty()){
states p = q.front();
q.pop();
if(p.local == k){
cout<<p.time<<endl;
return;
}
states temp;
for(int i = 0;i < 3;i++){
if(i == 0){
temp.local = p.local - 1;
}
if(i == 1){
temp.local = p.local + 1;
}
if(i == 2){
temp.local = p.local * 2;
}
if(temp.local < 0 || temp.local > MAX || visit[temp.local])
continue;
temp.time = p.time + 1;
visit[temp.local] = true;
q.push(temp);
}
}
}
int main(){
int n,k;
cin>>n>>k;
BFS(n,0,k);
return 0;
}
2.Find The Multiple
题目描述:
给定正整数 n,编写程序找出非零值 n 的倍数 m,并且 m 的十进制数表示仅包含数字0和1。你可以假设 n 不大于 200,且有不超过 100 位的相应 m。
#include <bits/stdc++.h>
using namespace std;
void BFS(int n){
int m = 1;
queue<int> q;
q.push(m);
while(!q.empty()){
int t = q.front();
q.pop();
if(t % n == 0) {
cout<<t<<endl;
return;
}
q.push(t * 10);
q.push(t * 10 + 1);
}
}
int main(){
int n;
while(cin>>n){
if(n == 0) break;
BFS(n);
}
return 0;
}
3.玛雅人的密码(清华大学复试上机题)
题目描述:
玛雅人有一种密码,字符串中出现连续的 2012 四个数字时就能解开密码。给定一个长度为N的字符串(2 ≤ N ≤ 13),该字符串中只含有 0, 1, 2 三种数字,问这个字符串要移位几次才能解开密码,每次只能移动相邻的两个数字。例如,02120 经过一次移位,可以得到20120, 01220, 02210, 02102,其中 20120 符合要求,因此输出为 1。如果无论移位多少次都解不开密码,输出-1。
#include <bits/stdc++.h>
using namespace std;
struct answer{
string s;
int move;
};
void BFS(int n,string str){
struct answer s;
s.s = str;
s.move = 0;
queue<answer> q;
q.push(s);
while(!q.empty()){
struct answer t = q.front();
q.pop();
if(t.s.find("2012") != string::npos){
cout<<t.move<<endl;
return;
}
struct answer temp;
char ch;
for(int i = 0;i < n-1;i++){
temp.s = t.s;
ch = temp.s[i];
temp.s[i] = temp.s[i+1];
temp.s[i+1] = ch;
temp.move = t.move + 1;
q.push(temp);
}
}
cout<<-1<<endl;
}
int main(){
int n;
string str;
cin>>n;
cin>>str;
BFS(n,str);
return 0;
}
2)DFS
1.马踏棋盘
题目描述:
骑士每天看着相同的黑白方块感到越来越无聊并决定去世界各地旅行。骑士按照“日”字规则行走。骑士的世界就是他生活的棋盘。骑士生活在比普通 8*8 棋盘更小的棋盘上,但棋盘的形状仍然是长方形的。你能帮助这位冒险骑士制订旅行计划吗? 找一条能够让骑士遍历棋盘上所有点的路径。骑士可以在任何一块方块上开始或结束他的旅行。
#include <bits/stdc++.h>
using namespace std;
const int MAX = 30;
int p,q;
bool visit[MAX][MAX];
int direction[8][2] = {
{-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}
};
bool DFS(int x,int y,int step,string ans){
if(step == p * q){
cout<<ans<<endl;
return true;
}
for(int i = 0;i < 8;i++){
int nx = x + direction[i][0];
int ny = y + direction[i][0];
char col = ny + 'A';
char row = nx + '1';
if(nx < 0 || nx >= p || ny < 0 || ny >= q || visit[nx][ny])
continue;
visit[nx][ny] = true;
if(DFS(nx,ny,step+1,ans+col+row)){
return true;
}
visit[nx][ny] = false;
}
return false;
}
int main(){
int n;
cin>>n;
int caseNumber = 0;
while(n--){
cin>>p>>q;
memset(visit,false,sizeof(visit));
visit[0][0] = true;
if(!DFS(0,0,1,"A1")){
cout<<"impossible"<<endl;
}
}
return 0;
}
2.Square
题目描述:
给出一堆长度各异的木棍,这些木棍能否头尾相连形成一个正方形?
#include <bits/stdc++.h>
using namespace std;
const int MAX = 25;
int side;
int m;
int sticks[MAX];
bool visit[MAX];
bool DFS(int sum,int number,int position){
if(number == 3) return true;
int sample = 0;
for(int i = position;i < m;i++){
if(visit[i] || sum + sticks[i] > side || sticks[i] == sample) continue;
visit[i] = true;
if(sum + sticks[i] == side){
if(DFS(0,number+1,0)) return true;
else sample = sticks[i];
}else{
if(DFS(sum + sticks[i],number,i+1)) return true;
else sample = sticks[i];
}
visit[i] = false;
}
return false;
}
bool compare(int x,int y){
return x > y;
}
int main(){
int n;
cin>>n;
while(n--){
int length = 0;
cin>>m;
for(int i = 0;i < m;i++){
cin>>sticks[i];
length += sticks[i];
}
memset(visit,false,sizeof(visit));
if(length % 4 != 0){
cout<<"no"<<endl;
continue;
}
side = length / 4;
sort(sticks,sticks+m,compare);
if(sticks[0] > side){
cout<<"no"<<endl;
continue;
}
if(DFS(0,0,0)){
cout<<"yes"<<endl;
}else{
cout<<"no"<<endl;
}
}
return 0;
}
3.神奇的口袋(北京大学复试上机题)
题目描述:
有一个神奇的口袋,其容积是 40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John 现在有 n 个想要得到的物品,每个物品的体积分别是 a1, a2,…, an。John 可以从这些物品中选择一些,如果选出的物体的总体积是 40,那么利用这个神奇的口袋John 就能得到这些物品。现在的问题是,John 有多少种选择物品的不同方式。
#include <bits/stdc++.h>
using namespace std;
int fun(int arr[],int n,int target){
if(target == 0) return 1;
if(n == 1) {
if(arr[0] == target) return 1;
else return 0;
}
/*
两种情况相加
1、调出一件,不选择
2、调出一件,选择这件
*/
return fun(arr,n-1,target) + fun(arr,n-1,target - arr[n-1]);
}
int main(){
int arr[20];
int n;
cin>>n;
for(int i = 0;i < n;i++)
cin>>arr[i];
cout<<fun(arr,n,40)<<endl;
return 0;
}
4.八皇后
题目描述:
会下国际象棋的人都很清楚:皇后可以在横线、竖线、斜线上不限步数地吃掉其他棋子。如何将8 个皇后放在棋盘上(有 8*8 个方格),使它们谁也不能被吃掉?这就是著名的八皇后问题。对于某个满足要求的 8 皇后的摆放方法,定义一个皇后串 a 与之对应,即a = b1b2…b8,其中bi 为相应摆法中第 i 行皇后所处的列数。已经知道 8 皇后问题一共有 92 组解(即92 个不同的皇后串)。给出一个数 b,要求输出第 b 个串。串的比较是这样的:皇后串 x 置于皇后串y 之前,当且仅当将 x 视为整数时比 y 小。
#include <bits/stdc++.h>
using namespace std;
vector<int> A;
int q[8][8] = {0};
bool judge(int row,int col){
for(int i = 0;i < 8;i++){
if(q[i][col] == 1) return false;
}
for(int i = row,j = col;i >= 0 && j >= 0;i--,j--){
if(q[i][j] == 1) return false;
}
for(int i = row,j = col;i >= 0 && j < 8;i--,j++){
if(q[i][j] == 1) return false;
}
return true;
}
void DFS(int row,int ans){
if(row == 8){
A.push_back(ans);
return;
}
for(int col = 0;col < 8;col++){
if(judge(row,col)){
q[row][col] = 1;
DFS(row+1,ans * 10 + col + 1);
//回溯
q[row][col] = 0;
}
}
}
int main(){
DFS(0,0);
sort(A.begin(),A.end());
int index;
while(cin>>index){
cout<<A[index - 1]<<endl;
}
return 0;
}
13、动态规划
1)递推求解
1.N 阶楼梯上楼问题(华中科技大学复试上机题)
题目描述:
N 阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式(要求采用非递归)。
#include <bits/stdc++.h>
using namespace std;
int main(){
int dp[90];
int n;
cin>>n;
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i <= n;i++){
dp[i] = dp[i-1] + dp[i-2];
}
cout<<dp[n]<<endl;
return 0;
}
2.吃糖果(北京大学复试上机题)
题目描述:
小明的妈妈从外地出差回来,给他带了一盒好吃又精美的巧克力(盒内共有N 块巧克力,20>N > 0)。妈妈告诉小明每天可以吃一块或两块巧克力。假设小明每天都吃巧克力,问小明共有多少种不同的吃完巧克力的方案。例如,如果 N = 1,那么小明第 1 天就吃掉它,共有1 种方案;如果 N = 2,那么小明可以第 1 天吃 1 块,第 2 天吃 1 块,也可以第 1 天吃2 块,共有2 种方案;如果 N = 3,那么小明第 1 天可以吃 1 块,剩 2 块,也可以第 1 天吃 2 块剩1 块,所以小明共有2+1 = 3 种方案;如果 N = 4,那么小明可以第 1 天吃 1 块,剩 3 块,也可以第1 天吃2 块,剩2块,共有 3 + 2 = 5 种方案。现在给定 N,请你写程序求小明吃巧克力的方案数目。
#include <bits/stdc++.h>
using namespace std;
int main(){
int dp[90];
int n;
cin>>n;
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i <= n;i++){
dp[i] = dp[i-1] + dp[i-2];
}
cout<<dp[n]<<endl;
return 0;
}
2)最大连续子序列和
1.最大序列和(清华大学复试上机题)
题目描述:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1000000;
long long arr[MAX];
long long dp[MAX];
int main(){
int n;
while(cin>>n){
for(int i = 0;i < n;i++)
cin>>arr[i];
long long maxN = arr[0];
dp[0] = arr[0];
for(int i = 1;i < n;i++){
dp[i] = max(arr[i], dp[i-1] + arr[i]);
maxN = max(maxN,dp[i]);
}
cout<<maxN<<endl;
}
return 0;
}
2.最大子矩阵(北京大学复试上机题)
题目描述:
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是 1*1)子矩阵。
#include <bits/stdc++.h>
using namespace std;
const int MAX = 100;
int matric[MAX][MAX];
int total[MAX][MAX];
int arr[MAX];
int dp[MAX];
int maxSequence(int n){
int maxN = arr[0];
for(int i = 0;i < n;i++){
if(i == 0) dp[i] = arr[i];
else dp[i] = max(arr[i], dp[i-1] + arr[i]);
maxN = max(maxN,dp[i]);
}
return maxN;
}
int maxSubMatric(int n){
int maxN = 0;
for(int i = 0;i < n;i++){
for(int j = i;j < n;j++){
for(int k = 0;k < n;k++){
if(i == 0) arr[k] = total[j][k];
else arr[k] = total[j][k] - total[i-1][k];
}
int current = maxSequence(n);
maxN = max(current,maxN);
}
}
return maxN;
}
int main(){
int n;
while(cin>>n){
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++)
cin>>matric[i][j];
}
//按列的累计和
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++){
if(i == 0)
total[i][j] = matric[i][j];
else
total[i][j] = total[i-1][j] + matric[i][j];
}
}
int answer = maxSubMatric(n);
cout<<answer<<endl;
}
return 0;
}
3.最大连续子序列(浙江大学复试上机题)
题目描述:
#include <bits/stdc++.h>
using namespace std;
int arr[10000];
int dp[10000];
int main(){
int n;
while(cin>>n){
if(n == 0) break;
for(int i = 0;i < n;i++)
cin>>arr[i];
int begin = 0,end = 0,t = 0,ans = arr[0];
for(int i = 0;i < n;i++){
if(i == 0) {
dp[i] = arr[i];
}else{
//延长之前序列
if(dp[i-1] + arr[i] > arr[i]){
dp[i] = dp[i-1] + arr[i];
//新起一段序列
}else{
dp[i] = arr[i];
t = i;
}
}
if(dp[i] > ans){
ans = dp[i];
begin = t;
end = i;
}
}
cout<<ans<<" "<<arr[begin]<<" "<<arr[end]<<endl;
}
return 0;
}
3)最长自增子序列
1.拦截导弹(北京大学复试上机题)
题目描述:
某国为了防御敌国的导弹袭击,开发了一种导弹拦截系统。但这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但以后每发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。
#include <bits/stdc++.h>
using namespace std;
int arr[25];
//dp[i]表示以A[i]作为末尾的最长递增子序列的长度
int dp[25];
int main(){
int n;
cin>>n;
for(int i = 0;i < n;i++)
cin>>arr[i];
int answer = 0;
for(int i = 0;i < n;i++){
dp[i] = 1;
for(int j = 0;j < i;j++){
if(arr[j] >= arr[i])
dp[i] = max(dp[i],dp[j]+1);
}
answer = max(answer,dp[i]);
}
cout<<answer<<endl;
return 0;
}
2.最大上升子序列和(北京大学复试上机题)
题目描述:
#include <bits/stdc++.h>
using namespace std;
int arr[1000];
int dp[1000];
int main(){
int n;
cin>>n;
for(int i = 0;i < n;i++)
cin>>arr[i];
int answer = 0;
for(int i = 0;i < n;i++){
dp[i] = arr[i];
for(int j = 0;j < i;j++){
if(arr[j] < arr[i])
dp[i] = max(dp[i],dp[j]+arr[i]);
}
answer = max(answer,dp[i]);
}
cout<<answer<<endl;
return 0;
}
3.合唱队形(北京大学复试上机题)
题目描述:
#include <bits/stdc++.h>
using namespace std;
/*
求从0开始、以i结束的最长递增序列
求从i开始、以n结束的最长递减序列
再根据以上两个数组求出以k为分割点的最长合唱队形
最后减法得出最少出列个数
*/
int main(){
int before[100],after[100],dp[100],height[100];
int n;
cin>>n;
for(int i = 0;i < n;i++)
cin>>height[i];
memset(before,0,sizeof(before));
memset(after,0,sizeof(after));
for(int i = 0;i < n;i++){
dp[i] = 0;
for(int j = 0;j < i;j++){
if(height[j] < height[i])
before[i] = max(before[i],before[j] + 1);
}
}
for(int i = n-1;i >= 0;i--){
after[i] = 0;
for(int j = n-1;j > i;j--){
if(height[j] < height[i])
after[i] = max(after[i],after[j] + 1);
}
}
int maxSelect = 0;
for(int i = 0;i < n;i++){
dp[i] = before[i] + after[i] + 1;
if(dp[i] > maxSelect) maxSelect = dp[i];
}
cout<<n - maxSelect<<endl;
return 0;
}
4)最长公共子序列
1.Common Subsequence
题目描述:
#include <bits/stdc++.h>
using namespace std;
int main(){
string s1,s2;
cin>>s1;
cin>>s2;
int dp[100][100];
int n = s1.size();
int m = s2.size();
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(s1[i-1] == s2[j-1])
//此时必定存在一个最长公共子串以s1[i]和s2[j]结尾
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
cout<<dp[n][m]<<endl;
return 0;
}
2.Coincidence(上海交通大学复试上机题)
题目描述:
找到两个字符串的最长公共字串。
#include <bits/stdc++.h>
using namespace std;
int main(){
string s1,s2;
cin>>s1;
cin>>s2;
int n = s1.size();
int m = s2.size();
vector<vector<int> > dp(n+1, vector<int>(m+1,0));
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(s1[i-1] == s2[j-1])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
cout<<dp[n][m]<<endl;
return 0;
}
5)背包问题
1.点菜问题(北京大学复试上机题)
题目描述:
北大网络实验室经常有活动需要叫外卖,但是每次叫外卖的报销经费的总额最大为C元,有N种菜可以点,经过长时间的点菜,网络实验室对于每种菜 i 都有一个量化的评价分数(表示这个菜的可口程度)Vi,每种菜的价格为 Pi,问如何选择各种菜,使得在报销额度范围内能使点到的菜的总评价分数最大。 注意:由于需要营养多样化,每种菜只能点一次。
#include <bits/stdc++.h>
using namespace std;
int main(){
//dp[i][j]表示前 i 个物品装进容量为j 的背包能获得的最大价值。
//dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
//0-1 背包的特点是,每种物品至多只能选择一件,即在背包中该物品的数量只有0 和1 两种情况,这也是0-1 背包名称的由来。
int c,n;
int dp[1001];
int v[1001];
int w[1001];
while(cin>>c>>n){
for(int i = 1;i <= n;i++){
cin>>w[i];
cin>>v[i];
}
memset(dp,0,sizeof(dp));
for(int i = 1;i <= n;i++){
for(int j = c;j >= w[i];j--)
dp[j] = max(dp[j],dp[j - w[i]] + v[i]);
}
cout<<dp[c]<<endl;
}
return 0;
}
2.最小邮票数(清华大学复试上机题)
题目描述:
有若干邮票,要求从中选取最少的邮票张数凑成一个给定的总值。 例如,有 1 分、3 分、3 分、3 分、4 分五张邮票,要求凑成 10 分;此时使用3 张邮票:3分、3 分、4 分即可。
#include <bits/stdc++.h>
using namespace std;
int main(){
int m,n;
//前i张邮票总价值为j
int dp[100][100];
vector<int> stamps;
int stamp;
cin>>m>>n;
for(int i = 0;i < n;i++){
cin>>stamp;
stamps.push_back(stamp);
}
for(int i = 0;i < 100;i++)
for(int j = 0;j < 100;j++)
dp[i][j] = 99999;
//需要的价值为0,则邮票张数也为0
for(int i = 0;i < 100;i++)
dp[i][0] = 0;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(stamps[i-1] <= j){
//选择
dp[i][j] = min(dp[i-1][j], dp[i-1][j-stamps[i-1]] + 1);
}else{
//不选择
dp[i][j] = dp[i-1][j];
continue;
}
}
}
if(dp[n][m] != 99999)
cout<<dp[n][m]<<endl;
else
cout<<0<<endl;
return 0;
}
3.Piggy-Bank
题目描述:
在 ACM 可以做任何事情之前,必须准备预算和获得必要的财务支持。该行动的主要收入来自不可逆转的捆绑资金(IBM)。背后的想法很简单。每当一些 ACM成员有任何小钱时,他将所有硬币扔进存钱罐。你知道这个过程是不可逆转的,即硬币不能在不打破存钱罐的情况下取出。经过足够长的时间,存钱罐里应该有足够的现金来支付需要支付的东西。
但是存钱罐存在一个很大的问题,那就是无法确定存钱罐内有多少钱。也许打破存钱罐后会发现没有足够的钱。显然,我们希望避免这种不愉快的情况。唯一可以做的是称存钱罐的重量并试图猜测里面有多少硬币。假设我们能够确切地确定存钱罐的重量,并且我们知道给定货币中所有硬币的重量。然后通过存钱罐的重量可以保证存钱罐中的最小存款。你的任务是找出最坏的情况,并确定存钱罐内的最小存款。我们需要你的帮助,以便不再过早地打破存钱罐!
#include <bits/stdc++.h>
using namespace std;
/*
完全背包问题:有n种物品,每种物品的重量为w[i],其价值为v[i],每种物品的数量均为无限个,
现在有容量为m的背包,如何选择物品使得装入背包物品的价值最大?
dp[i][j] = max(dp[i-1][j], dp[i][j- w[i]] + v[i])
dp[j] = max(dp[j], dp[j - w[i]] + v[i])
*/
const int INF = INT_MAX / 100;
const int MAXN = 1000;
int dp[MAXN];
int v[MAXN];
int w[MAXN];
int main(){
int caseNumber;
cin>>caseNumber;
while(caseNumber--){
int e,f;
cin>>e>>f;
//背包容量
int m = f - e;
//物品种类
int n;
cin>>n;
for(int i = 0;i < n;i++){
cin>>v[i]>>w[i];
}
memset(dp,INF,sizeof(dp));
dp[0] = 0;
for(int i = 0;i < n;i++)
for(int j = w[i];j <= m;j++)
dp[j] = min(dp[j], dp[j - w[i]] + v[i]);
if(dp[m] >= INF)
cout<<"This is impossible."<<endl;
else
cout<<"the piggy-bank is"<<dp[m]<<endl;
}
return 0;
}
4.珍惜现在,感恩生活
题目描述:
急!灾区的食物依然短缺! 为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食来支援灾区。现在假设你一共有资金 n 元,而市场有 m 种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。请问:你用有限的资金最多能采购多少千克粮食呢?
#include <bits/stdc++.h>
using namespace std;
/*
完多重背包问题:有 n 种物品,每种物品的重量为 w[i],其价值为 v[i],每种物品的数量为k[i],
现在有个容量为 m 的背包,如何选择物品使得装入背包物品的价值最大。
*/
const int INF = INT_MAX / 100;
const int MAXN = 1000;
int dp[MAXN];
int v[MAXN];
int w[MAXN];
int k[MAXN];
int value[MAXN]; //分解后物品价值
int weight[MAXN]; //分解后物品质量
int main(){
int C;
cin>>C;
int n,m;
while(C--){
cin>>m>>n;
int num = 0; //分解后商品的数量
for(int i = 0;i < n;i++){
cin>>w[i];
cin>>v[i];
cin>>k[i];
for(int j = 1;j <= k[i];j <<= 1){
value[num] = j * v[i];
weight[num] = j * w[i];
num++;
k[i] -= j;
}
if(k[i] > 0){
value[num] = k[i] * v[i];
weight[num] = k[i] * w[i];
num++;
}
}
for(int i = 0;i <= m;i++)
dp[i] = 0;
for(int i = 0;i < num;i++){
for(int j = m;j >= weight[i];j--)
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
cout<<dp[m]<<endl;
}
return 0;
}
6)其他问题
1.放苹果(北京大学复试上机题)
题目描述:
把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法(用 K 表示)?例如,5, 1, 1 和 1, 5, 1 是同一种分法。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 15;
int dp[MAXN][MAXN];
int main(){
int m,n;
cin>>m>>n;
//没有盘子
for(int i = 1;i <= m;i++) dp[i][0] = 0;
//没有苹果
for(int i = 0;i <= n;i++) dp[0][i] = 1;
for(int i = 1;i <= m;i++){
for(int j = 1;j <= n;j++){
if(j > i) dp[i][j] = dp[i][i]; //一定有空盘子
else dp[i][j] = dp[i-j][j] + dp[i][j-1]; //放满每一个盘子 + 至少有一个空盘子
}
}
cout<<dp[m][n]<<endl;
return 0;
}
2.整数拆分(清华大学复试上机题)
题目描述:
一个整数总可以拆分为 2 的幂的和。例如,7 可以拆分成 7 = 1 + 2 + 4,7 = 1 + 2 + 2 + 2,7 =1+1 + 1 + 4,7 = 1 + 1 + 1 + 2 + 2,7 = 1 + 1 + 1 + 1 + 1 + 2,7 = 1 + 1 + 1 + 1 + 1 + 1 + 1,共有6种不同的拆分方式。再如,4 可以拆分成 4 = 4,4 = 1 + 1 + 1 + 1,4 = 2 + 2,4 = 1 + 1 + 2,共有4 种不同的拆分方式。 用 f (n) 表示 n 的不同拆分的种数,例如 f (7) = 6。 要求编写程序,读入n(不超过1000000),输出 f (n) %1000000000。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int dp[1000000] = {0};
dp[1] = 1;
for(int i = 2;i <= n;i++){
if(i % 2 == 0)
//拆分为包含1和不含1的两种情形
dp[i] = (dp[i - 1] + dp[i/2]) % 1000000000;
else
dp[i] = dp[i-1] % 1000000000;
}
cout<<dp[n]<<endl;
return 0;
}