题目描述
一个整数总可以拆分为2的幂的和,例如: 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 总共有六种不同的拆分方式。 再比如:4可以拆分成:4 = 4,4 = 1 + 1 + 1 + 1,4 = 2 + 2,4=1+1+2。 用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。
输入描述:
每组输入包括一个整数:N(1<=N<=1000000)。
输出描述:
对于每组数据,输出f(n)%1000000000。
问题分析
本题主要思路是分奇偶递推。当f[n]表示奇数n的拆分种数时,f[n-1]就是偶数n-1的拆分种数,易知奇数的拆分形式中至少含有一个1,故f[n] = f[n-1];而当n是偶数时,分为两种情况:a.当n的拆分形式中包含1,此时f[n] = f[n-1];b.当n的拆分形式中不包含1,n = n/2 + n/2,此时f[n] = f[n/2].综合两种情况,有f[n] = f[n-1]+f[n/2]。
AC代码
#include<iostream>
using namespace std;
#include<bits/stdc++.h>
const int N = 1e6+5;
const int mod = 1e9;
int dp[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i < N;i++){
if(i&1){
dp[i] = dp[i-1]%mod;
}
else{
dp[i] = (dp[i-1]+dp[i/2])%mod;
}
}
int n;
while(cin>>n){
cout<<dp[n]<<endl;
}
return 0;
}
题目描述
玛雅人有一种密码,如果字符串中出现连续的2012四个数字就能解开密码。给一个长度为N的字符串,(2=<N<=13)该字符串中只含有0,1,2三种数字,问这个字符串要移位几次才能解开密码,每次只能移动相邻的两个数字。例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1.如果无论移位多少次都解不开密码,输出-1。
输入描述:
输入包含多组测试数据,每组测试数据由两行组成。
第一行为一个整数N,代表字符串的长度(2<=N<=13)。
第二行为一个仅由0、1、2组成的,长度为N的字符串。
输出描述:
对于每组测试数据,若可以解出密码,输出最少的移位次数;否则输出-1。
问题分析
本题是一个典型的bfs。节点就是一个字符串,与它相邻的节点就是交换一次所得到的字符串。初始节点出发,看看它交换一次形成的字符串里哪些没出现过,这些字符串如果有符合要求的就结束BFS返回结果,如果不符合要求那就把它们加入队列。遍历完之后还是没碰到符合要求的字符串就返回-1。
AC代码
#include<iostream>
using namespace std;
#include<bits/stdc++.h>
string s;
int n;
int tot;
struct node{
string s;
int id;
};
int bfs(){
if(s.find("2012") != s.npos){
return tot;
}
queue<node>q;
node u,v;
u.s = s;
u.id = 0;
q.push(u);
while(!q.empty()){
u = q.front();
q.pop();
for(int i = 1;i < n;i++){
v.s = u.s;
swap(v.s[i],v.s[i-1]);
v.id = u.id+1;
if(v.s.find("2012")!=v.s.npos){
return v.id;
}
q.push(v);
}
}
return -1;
}
int main(){
int a[4];
while(cin>>n){
tot = 0;
cin>>s;
for(int i = 0;i < n;i++){
a[s[i]-'0']++;
}
if(a[0] < 1 || a[1] < 1 || a[2] < 2){
cout<<"-1"<<endl;
return 0;
}
tot = bfs();
cout<<tot<<endl;
}
}
题目描述
有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。 如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。
输入描述:
有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。
输出描述:
对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。
问题分析
本题是一道典型的0-1背包问题。 对于每一个dp数组来说,dp[i]中的i代表总值,dp[i]中保存的数据为个数,遍历所有小于等于m的i,如果总值减去当前所选的邮票的面值,即i-c[i]合法并且可达那么i一定可达,并且dp[i]中的值应该是dp[i-c[i]]的值+1和原来的值中较小的一个。特别注意0-1背包和完全背包的区别,j从m->0为倒序。
AC代码
#include<iostream>
using namespace std;
#include<bits/stdc++.h>
typedef long long ll;
const int N = 1e5+5;
const int INF = 1e9+7;
int dp[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int a[N];
int n,m;
while(cin>>m>>n){
for(int i = 0;i <n;i++){
cin>>a[i];
}
for(int i = 1;i <= m;i++){
dp[i] = INF; //想想初始化为INF的原因
}
dp[0] = 0;
for(int i = 0;i < n;i++){
for(int j = m;j >= 0;j--){
if(j-a[i] >= 0&& dp[j-a[i]] != INF){
dp[j] = min(dp[j],dp[j-a[i]]+1);
}
}
}
if(dp[m]==INF)cout<<0<<endl;
else cout<<dp[m]<<endl;
}
return 0;
}
题目描述
N<k时,root(N,k) = N,否则,root(N,k) = root(N’,k)。N’为N的k进制表示的各位数字之和。输入x,y,k,输出root(x^y,k)的值 (这里^为乘方,不是异或),2=<k<=16,0<x,y<2000000000,有一半的测试点里 x^y 会溢出int的范围(>=2000000000)
输入描述:
每组测试数据包括一行,x(0<x<2000000000), y(0<y<2000000000), k(2<=k<=16)
输出描述:
输入可能有多组数据,对于每一组数据,root(x^y, k)的值
问题分析
对于root(N, k)中的N,我们可以把N看作关于k的多项式,也就是N = a0 + a1k + a2k^2 + … + an* k^n,而我们要求的root函数就是这个多项式的系数和,也就是a0 + a1 + a2 + … + an。
下面我们考虑root(N^2, k)。此时N^2 = (a0 + a1k + a2k^2 + … + an* kn)2,而这个多项式展开后的系数和是(a0 + a1 + a2 + … + an)^2,这个结果刚好就是先对N取root函数再平方的结果。实际上,我们很容易就能看出,多项式先乘方再取系数和(先乘再去掉k)与先取系数和再乘方(先去掉k再乘),结果是一样的(因为有没有k并不影响系数间的相乘,也不影响相乘之后的求和),于是乎,我们可以得到以下的递推公式:
- root(x, y, k) = root((root(x, y / 2, k))^2, 1, k), y为偶数
- root(x, y, k) = root((root(x, y / 2, k))^2 * root(x, 1, k), 1, k), y为非1的奇数
- root(x, 1, k) = x % k + x / k % k + …(大于k的话再重复求root)
AC代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y,z;
ll solve(ll a){
ll sum = 0;
if(a < z){
return a;
}
while(a){
sum += a%z;
a /= z;
}
return solve(sum);
}
int main(){
while(cin>>x>>y>>z){
ll a = 1;
ll tmp = solve(x);
while(y){
if(y % 2){
a = solve(tmp*a);
}
tmp = solve(tmp*tmp);
y /= 2;
}
cout<<a<<endl;
}
}
题目描述
在某条线路上有N个火车站,有三种距离的路程,L1,L2,L3,对应的价格为C1,C2,C3.其对应关系如下: 距离s 票价 0<S<=L1 C1 L1<S<=L2 C2 L2<S<=L3 C3 输入保证0<L1<L2<L3<109,0<C1<C2<C3<109。 每两个站之间的距离不超过L3。 当乘客要移动的两个站的距离大于L3的时候,可以选择从中间一个站下车,然后买票再上车,所以乘客整个过程中至少会买两张票。 现在给你一个 L1,L2,L3,C1,C2,C3。然后是A B的值,其分别为乘客旅程的起始站和终点站。 然后输入N,N为该线路上的总的火车站数目,然后输入N-1个整数,分别代表从该线路上的第一个站,到第2个站,第3个站,……,第N个站的距离。 根据输入,输出乘客从A到B站的最小花费。
输入描述:
以如下格式输入数据:
L1 L2 L3 C1 C2 C3
A B
N
a[2]
a[3]
……
a[N]
输出描述:
可能有多组测试数据,对于每一组数据,根据输入,输出乘客从A到B站的最小花费。
问题分析
本题主要就是一个动态规划问题。要求从a到b的最小代价。初始值dp[a] = 0;状态转移方程为:** dp[j] = min(dp[j],dp[i]+cost(abs(L[j]-L[i])));**可以选择在j点下车或者不下车。dp[i]表示到达i地是所需的最小代价。
AC代码
#include<iostream>
using namespace std;
#include<bits/stdc++.h>
const int N = 1e5+5;
int L[N];
int l1,l2,l3,c1,c2,c3,s;
int n;
int a,b;
const int INF = 1e9+7;
int cost(int s){
if(s <= l1){
return c1;
}
else if(s <= l2 && s > l1){
return c2;
}
else{
return c3;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int dp[N];
while(cin>>l1>>l2>>l3>>c1>>c2>>c3){
cin>>a>>b>>n;
for(int i = 2;i <= n;i++){
cin>>L[i];
}
for(int i = 0;i < N;i++){
dp[i] = INF;
}
dp[a] = 0; //始发地代价为0元
for(int i = a;i < b;i++){
for(int j = a+1;j <= b&&L[j]-L[i]<=l3;j++){
//可以选在在j处下车or不下车
dp[j] = min(dp[j],dp[i]+cost(abs(L[j]-L[i])));
}
}
cout<<dp[b]<<endl;
}
return 0;
}