2022/06/15做题

1.剩下的树

描述:
有一个长度为整数L(1<=L<=10000)的马路,可以想象成数轴上长度为L的一个线段,起点是坐标原点,在每个整数坐标点有一棵树,即在0,1,2,…,L共L+1个位置上有L+1棵树。 现在要移走一些树,移走的树的区间用一对数字表示,如 100 200表示移走从100到200之间(包括端点)所有的树。 可能有M(1<=M<=100)个区间,区间之间可能有重叠。现在要求移走所有区间的树之后剩下的树的个数。
输入描述:
两个整数L(1<=L<=10000)和M(1<=M<=100)。 接下来有M组整数,每组有一对数字。
输出描述:
可能有多组输入数据,对于每组输入数据,输出一个数,表示移走所有区间的树之后剩下的树的个数。
示例1:
输入:
500 3
100 200
150 300
470 471
输出:
298
看到考察的知识点是哈希和数组还有栈,哈希还没复习。
试着用别的方法做一下。
建立数据结构Node,用于表示移除的每一对整数。start,end,表示从start->end的树都会被移除。
思路:
1.首先接受输入,利用优先队列q排序,重载运算符<,使start较小的那一组在队头。这样可以简化后期合并操作。
2.建立栈s2。将q的队头第一个Node入栈s2。
3.然后每次观察q的队头和s2的栈顶元素,能不能合并。如果能合并,合并。将q的队头出队,s2的栈顶出栈,将合并后的Node入栈。如果不能合并,就说明两段无交集,q的队头入栈s2。
4.重复上述操作,知道优先队列q为空。
5.剩下的就简单了,遍历堆栈s2,计算移除的总棵树。最后拿L一减就行了。

#include<iostream>
#include<queue>
#include<stack>
using namespace std;
typedef struct Node{
    int start;
    int end;
    Node(){}
    Node(int s,int e):start(s),end(e){}
    bool operator< (Node n) const{
        return start>n.start;   //start小的在优先队头
    }
};
int jiaocha(Node a,Node b){
    if(b.start>=a.start && b.start<=a.end){
        return 1;   //相交   
    }else{
        return 0;   //不相交
    }
}
int main(){
    priority_queue<Node> q;
    stack<Node> s2;
    int L,M;
    int start,end;  //要移走编号为start->end的树。
    int tree_num;  //要移走的树的个数
    Node tmp = Node();
    Node new_push = Node();
    while(cin>>L>>M){
        while(M--){
            cin>>start>>end;
            Node node = Node(start,end);
            q.push(node);
        }
        s2.push(q.top());
        q.pop();
        while(!q.empty()){
            if(jiaocha(s2.top(),q.top())){  //二者交叉
                tmp = s2.top();
                new_push = Node(s2.top().start,(tmp.end>q.top().end)?tmp.end:q.top().end);
                q.pop();
                s2.pop();
                s2.push(new_push);
            }else{   //二者不交叉
                s2.push(q.top());
                q.pop();
            }
        }
        tree_num = 0;
        while(!s2.empty()){   //统计个数
            tree_num += s2.top().end-s2.top().start+1;
            s2.pop();
        }
        cout<<L+1-tree_num<<endl;
    }
    return 0;
}

2.最小邮票数

描述
有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。 如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。
输入描述:
有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。
输出描述:
对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。
示例1:
输入:
10
5
1 3 3 3 4
输出:
3
思路:0-1背包
将邮票价值从大到小排列。如实例1中,第一枚邮票为4角,第二枚邮票为3角,第三枚邮票为3角…
建立二维数组dp[j][i]表示价值为j,只用前i张邮票表示所用的最小邮票数。如果价值为j不能用前i张邮票表示,则dp[j][i] = -1。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#define M 100
#define N 20
int dp[M][N];

using namespace std;
int main(){
    int stamp_count,total_value;
    while(cin>>total_value>>stamp_count){
        int stamp_value[stamp_count];
        for(int i=stamp_count-1;i>=0;i--){
            cin>>stamp_value[i];
        }
        for(int i=0;i<M;i++){
            for(int j=0;j<N;j++){
                dp[i][j] = -1;   //初始化
            }
        }
        for(int i=0;i<stamp_count;i++){
            for(int j=0;j<=total_value;j++){
                if(j == 0){
                    dp[j+stamp_value[i]][i] = 1;   //只使用第i张邮票,对应价钱可以由1张邮票组成。
                }else if(i>=1 && dp[j][i-1]>0){
                    dp[j][i] = dp[j][i-1];   //某价值可由价值最大的前i-1张邮票中的n张组成,则一定可以由价值最大的i张邮票中的n张组成,且n为组成该价值的最小邮票数。
                    //下面根据凑成该价值是否含有第i张邮票进行分类
                    if(stamp_value[i]+j<=total_value && dp[j+stamp_value[i]][i-1]>=1){  //如果某价值能由前i-1张邮票的若干张凑成,则需比较由前i-1张邮票的若干张凑成该价值的最小张数和由前i张邮票的若干张凑成该价值的最小邮票数哪个小
                        dp[j+stamp_value[i]][i] = min(dp[j+stamp_value[i]][i-1],dp[j][i-1]+1);
                    }else if(stamp_value[i]+j<=total_value){  //如果某价值不能由前i-1张邮票的若干张凑成,必须选择第i张邮票才能凑成该价值。
                        dp[j+stamp_value[i]][i] = dp[j][i-1]+1;
                    }
                }
            }
        }
        if(dp[total_value][stamp_count-1]!=-1){
            cout<<dp[total_value][stamp_count-1]<<endl;
        }else{
            cout<<0<<endl;
        }
    }
    return 0;
}

3.求root(N,k)

描述
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)的值
示例1
输入:
4 4 10
输出:
4
思路:
1.运用结论:
设w为x在k进制下表示的各位数字之和的y次幂。
当x^y>k时:
root(x^y,k) = root(w^y,k)。(1)
重复使用(1)式,可以使w<k。这样就缩小了底数。
2.然后想到利用快速幂缩小指数。比如
2^10000 = 45000=82500。这样计算幂的算法时间复杂度为O(logn)。指数缩小以后,底数又会变大,不要紧,利用1中的结论,再次将底数等价转换为k禁止下底数的各位和,这样底数又缩小了。
这个方法可同时缩小指数和底数,来进行运算。

#include<iostream>
using namespace std;
typedef long long ll;
ll jinzhi(ll ans,ll k){   //底数转换为k进制的各位和计算
    ll result = 0;
    while(ans>0){
        result += ans%k;
        ans/=k;
    }
    return result;
}
ll quick_mi(ll x,ll y,ll k){
    while(x>=k){
        x = jinzhi(x,k);   //上来先缩小一次底数,将底数缩小为小于k。
    }
    ll ans = 1;
    while(y){
        if(y%2 == 1){
            ans *= x;     
        }
        x = x*x;
        x = jinzhi(x,k);   //1.缩小底数
        y/=2;   //2.快速幂缩小指数
    }
    return ans;
}
ll root(ll ans,ll k){   //递归
    if(ans<k){
        return ans;
    }else{
        return root(jinzhi(ans,k),k);
    }
}
int main(){
    ll x,y,k;
    while(cin>>x>>y>>k){
        cout<<root(quick_mi(x,y,k),k)<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值