刷题系列一

题目描述

一个整数总可以拆分为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值