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为结果。