1.切香肠
(kusac.pas/c/cpp)
【问题描述】
Jzyz的某次期中考试奖学金是由火腿肠企业赞助的,于是,奖学金变成了奖品,并且是N根长度完全相同的火腿肠。
本次考试一共M名同学获奖,学校决定,火腿肠全部平均分配,小x因为考试成绩很好,于是被选派为分火腿肠的人。
小x现在有一把很精确的刀,他可以一刀切掉火腿肠的任意需要的指定部分,小x现在想知道,如何用最少切的次数,完成火腿肠的分配。
比如,现在有2根火腿肠,有6个人获奖,那么,每根火腿肠需要切2刀,这样4刀就将火腿肠等分为6等份。
如果有3根火腿肠,4个人获奖,那么小x只需要把每根火腿肠切掉1/4,这样有3个人拿到每根的3/4,有一个人拿到3小块的1/4.这样最少3刀也能等分。
现在你需要帮助小x完成这个任务。
【输入】
两个整数N和M
【输出】
一个整数,最少切多少次可以平分。
【输入输出样例1】
kusac.in |
kusac.out |
2 6 |
4 |
【输入输出样例2】
kusac.in |
kusac.out |
3 4 |
3 |
【输入输出样例3】
kusac.in |
kusac.out |
6 2 |
0 |
【数据范围】
30% 1 ≤ N, M ≤ 16
100% 数据(1 ≤ N, M ≤ 100)
题解:
Kusac 火腿肠
锻炼思维的一道题目,我不认为简单,我拿到这道题的时候,我想了很久很久,还是没想出来,可能我的数学思维不行。
我觉得一个思考方案是,将N根火腿肠放在一起,假设是一根的话,那么切M-1刀,就肯定得到了M根。
但是,如果要切这M-1刀中,恰好有k刀是N根火腿肠的分割处,那么就不用切了。那么恰好会有多少分割处是要切的呢?
就是M和N的最大公约数-1次,
所以 结论就是 M-1-(gcd(M,N)-1)= M- gcd(M,N)
本道题数据量很小,求最大公约数很好求,不过我们一般求最大公约数都用欧几里得算法。
cpp:
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long m,n,sum=0;
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
int main()
{
freopen("kusac.in","r",stdin);
freopen("kusac.out","w",stdout);
cin>>n>>m;
sum=m-1-(gcd(m,n)-1);
cout<<sum<<endl;
return 0;
}
2.赛车
(car.pas/c/cpp)
【问题描述】
Jzyz的老师们要结伴出游了,并且他们到了一个很宽阔的赛车场,并且赛车场两边的风景是极好的,所以赛车不重要,看风景最重要。
现在jzyz有N名老师要参加看风景赛车,编号为1..N,赛车场有M条(1<=M<=N)条车道,编号为i的老师自己的车速最大为Si(1<=Si<=10^6)。
在经历很多次模拟驾驶之后,老师们为了避免碰撞,于是规定,每条车道上,如果某位老师前面有k个老师,那么这位老师的车速上限就会下降k*D个单位,也就是说第i个老师的开车的速度不会超过Si-k*D(0<=D<=5000),当然,如果这个数是负的,这个老师的速度将是0.当然只要遵循这个规则,老师们肯定就不会发生碰撞。
但是,最后老师们才发现,赛车场有规定,在赛道上行驶的车辆速度不得低于L(1<=L<=10^6)。
给定所有的数据,现在请你帮老师们计算一下,最多有多少个老师可以完成比赛,并欣赏到风景。
【输入】
第一行四个整数N,M,D,L。
接下来N行,每行一个整数,第i行为Si,表示第i位老师的车速上限。
【输出】
一个整数,最多有多少老师可以完成比赛。
【输入输出样例】
car.in |
car.out |
3 1 1 5 5 7 5 |
2 |
有3个老师,1个赛道,第一个和第二个老师可以参加比赛。
【数据范围】
50% 保证N<=100;
100% N<=50000,其余如题目描述
题解:
Car
这道题有一个贪心的规则,不要问我怎么想出来的,我只能说是经验,或者感觉。
不要问我怎么证明,考试的时候只要想不出反例,我们就努力去写。
首先,肯定按照速度排序是肯定的,这个肯定能想到;
然后,为了让更多的老师参加比赛,速度慢的肯定放前面,速度快的肯定放后面。
那样就按照速度从小到大,依次让老师按照速度从小到大的顺序从1,2,3..M号跑道上为第一个,第二个,能参加就参加,不能参加就不参加,这样就能保证让最多的人参加。
一个for循环的验证即可。
这种题就是需要自己设计策略的问题,而如何设计策略,只能靠自己的建模能力和经验,如何培养建模能力和经验,除了多做题,多思考之外,我觉得反思和总结比一味的看通过率和排名要更加重要。
对于贪心的策略的总结,将是一个永恒的话题。
cpp:
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=500000;
long long n,m,d,l,sum=0;
long long s[maxn],road[maxn];
void work(long long x,long long dao)
{
if(dao>m)
return;
long long speed;
speed=x-road[dao]*d;
if(speed>=l)
{
sum++;
road[dao]++;
return;
}
else
work(x,dao+1);
}
int main()
{
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m>>d>>l;
for(int i=1;i<=n;i++)
cin>>s[i];
memset(road,0,sizeof(road));
sort(s+1,s+1+n);
for(int i=1;i<=n;i++)
{
if(s[i]>=l)
{
work(s[i],1);
}
}
cout<<sum<<endl;
return 0;
}
3.排序
(sort.pas/c/cpp)
【问题描述】
小x在学习了各种排序算法后,决定自己设计一个排序算法。
他设计的排序算法有两种操作:
1) 挑出某个数,将其放在序列的最前面;
2) 挑出某个数,将其放在序列的最后面;
现在小x想知道,在最优情况下,按上面的操作将n个数排序完毕,最少的操作次数是多少?
给定一个序列:{8,12,25,7,15,19}
只需要将7放在前面,25 放在后面,两个操作即可。
【输入】
第一行一个数N,表示有N个数需要排序。
第二行N个整数,用空格隔开,表示要排序的N个数
【输出】
一个整数表示需要的操作次数。
【输入输出样例1】
sort.in |
sort.out |
6 8 12 25 7 15 19 |
2 |
【数据范围】
对于30% 的数据N≤50;
对于60% 的数据N≤1 000;
对于100% 的数据N≤100 000,且保证排序的每个数都不相同。
题解:
Sort 有一定思维难度
思考,不用进行操作的数,在最终序列里是什么样子。并且这个序列自然是越长越好。
我们需要找的是,在结果中单调递增,并且在原序列 中 以这个顺序存在的最长的一段。
比如样例:
812 25 7 15 19
我们发现 8 12 15 19 是最长的,所以,不需要改变位置的是4个数,需要改变的是 6-4=2个。
所以,本题就是找到一个排完序后是连续的,且在原序列中下标也是递增的最长的序列。
所以,本题就是一个排序+统计的过程。
具体如下:
1 2 3 4 5 6
812 25 7 15 19
排序之后,位置是
23 6 1 4 5
具体统计排序前在其前面,且排序后也在其前面并且连续的序列的长度为
12 1 1 3 4
所以最长的长度为 4。
cpp:
无
先sort,然后找出最大升序列 n-max为结果。