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;
}