1.天平
时空限制
CPU占用时长: 1秒
内存使用限制: 128MB
问题描述
已知一个天平 ,有N(1<=N<=1000)个已知质量的砝码(所有砝码质量的数值都在31位二进制内)。所称物体在天平的某一边,天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是物体的质量(物体和砝码不能放到同一边)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1<=C<2^30)时,天平就会被损坏。
砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。
用这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。
现在告诉你每个砝码的质量,以及天平能承受的最大质量。你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。
输入格式
第1行: 两个用空格隔开的正整数,N和C。
第2..N+1行: 每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。
输出格式
一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。
输入输出样例
样例
输入样例
3 15
1
10
20
输出样例
11
思路:
在N个砝码中,选任意个,使选中的砝码的质量和不超过C且最接近C,1<=N<=1000,1<=C<=2^30,搜索超时。
其实数据没有那么大,给出的N个单调不减队列,且任意一个都比它前两个数的和大。
因此要让这N个数每个都最小的话,这N个数至少构成斐波那契数列,斐波那契数列的第47项就已经大于2^30,所以有效的N<=46,但是如果直接这样搜下去,时间复杂度是O(2^n),仍然超时,所以要思考如何优化
我们从大的砝码往小的砝码搜索,且把选放在不选前面。如果在某一时刻,当前取到的
砝码的质量加上前面没有决定选不选的砝码的质量和都没有已经得到的答案更优,那么就不必在继续下去了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=60;
int n,c;
ll ans,w[maxn+1],sum[maxn+1];
void find(int crt,long long tot){
if(!crt) return;
if(tot+sum[crt]<=ans) return ;
if(tot+w[crt]<=c){
ans=max((ll)ans,tot+w[crt]);
find(crt-1,tot=w[crt]);
}
find(crt-1,tot);
}
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>w[i];
sum[i]=sum[i-1]+w[i];
}
find(n,0);
cout<<ans;
return 0;
}
2.数字变换
时空限制
CPU占用时长: 1秒
内存使用限制: 128MB
问题描述
给定一个数N (O≤N≤100000),变成另一个数K(O≤K≤100000),允许的操作是乘以2,或者加减1,问最少要几步才能完成?
输入格式
仅有两个整数 N 和 K。
输出格式
一个整数,表示需要的最少步数。
输入输出样例
样例
输入样例
5 17
输出样例
14
思路:深搜还是宽搜?
求最少步数,所以选择宽搜
优化1:如果n>=k,那么最少步数一定为n-k。
优化2:什么情况下没有必要+1?当前值+1>k就没有必要+1了
什么情况下没有必要*2?当前值>k就没有必要*2了
#include<bits/stdc++.h>
using namespace std;
const int maxn=100000;
struct Node{
int val,ste;
Node(int v,int s){val=v;ste=s;}
};
int n,k,ans;
bool f[maxn<<1|1];
queue<Node> q;
int bfs(){
f[n]=1;
q.push(Node(n,0));
while(q.size()){
Node crt=q.front();q.pop();
if(crt.val==k) return crt.ste;
if(!f[crt.val+1] && crt.val+1<=k)
f[crt.val+1]=1,q.push(Node(crt.val+1,crt.ste+1));
if(!f[crt.val-1] && crt.val>1)
f[crt.val-1]=1,q.push(Node(crt.val-1,crt.ste+1));
if(crt.val && crt.val<k && !f[crt.val<<1])
f[crt.val<<1]=1,q.push(Node(crt.val<<1,crt.ste+1));
}
}
int main(){
cin>>n>>k;
if(n>=k) cout<<n-k<<endl;
else cout<<bfs()<<endl;
return 0;
}
3.Ackerman函数
时空限制
CPU占用时长: 1秒
内存使用限制: 128MB
问题描述
计算ackerman函数值:
输入格式
从文件ackerman.in读入数据,第一行为两个数,即M和N,其中0<=M<=3,0<=N<=11。
输出格式
输出ack(m,n)的值。
输入输出样例
样例
输入样例
0 1
输出样例
2
3.#include<bits/stdc++.h>
using namespace std;
const int maxn=5e6;
int m,n;
int f[4][maxn+1];
int ack(int m,int n){
if(f[m][n]) return f[m][n];
if(!m) return f[m][n]=n+1;
if(!n) return f[m][n]=ack(m-1,1);
return f[m][n]=ack(m-1,ack(m,n-1));
}
int main(){
cin>>m>>n;
cout<<ack(m,n);
}
作业:(后期给出题解)
4.棋盘
时空限制
CPU占用时长: 5秒
内存使用限制: 256MB
问题描述
有一个m × m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。
任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费1个金币。
另外,你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。
现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
输入格式
数据的第一行包含两个正整数 m, n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。
接下来的 n 行,每行三个正整数 x, y, c, 分别表示坐标为( x, y)的格子有颜色 c。
其中 c=1 代表黄色, c=0 代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为( 1, 1),右下角的坐标为( m, m)。
棋盘上其余的格子都是无色。保证棋盘的左上角,也就是( 1, 1) 一定是有颜色的。
输出格式
输出一行,一个整数,表示花费的金币的最小值,如果无法到达,输出-1。
输入输出样例
样例1
输入样例
5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0
输出样例
8
样例2
输入样例
5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0
输出样例
-1
5.八数码
时空限制
CPU占用时长: 1秒
内存使用限制: 128MB
问题描述
在3×3的棋盘上,摆有8个棋子,每个棋子上标有1~8的某个数字。棋盘中留有一个空格(用0表示)。空格周围的4个棋子可以移到空格中。
需要你求解的问题是:给出一种棋盘的初始布局和目标布局,找出实现从初始布局到目标布局转变的最小步数。
例如,想实现下面左边的状态转变到右边的状态,则输入283104765 123804765,输出:4。具体步骤为:8下、2右、1上、8左。
输入格式
第一行一个9位字符串,表示初始局面。
第二行一个9位字符串,表示目标局面。
输出格式
一行一个整数,表示最小步数。
如果没有一个方案,则输出‘impossible’。(不包括引号)
输入输出样例
样例
输入样例
283104765
123804765
输出样例
4