1146: 【C语言训练】字符串正反连接
所给字符串正序和反序连接,形成新串并输出
输入:
任意字符串(长度<=50)
输出:
字符串正序和反序连接所成的新字符串
简单题,我看个别同学做的太繁了,正着输出一次,再倒着输出一次就ok了,不需要引进第三个string类变量
1838: Euler theorem
题目描述:
HazelFan有两个正整数a,b,他想计算a mod b。 但现在他忘了b的值,只记得a的值,请告诉他不同的可能的求余结果的数量。
输入:
第一行包含一个正整数T(1≤T≤5),表示测试样例数。对于每个测试样例:一行,包含正整数a(1≤a≤1e9)。
输出:
对于每个测试用例:单行包含一个非负整数,表示答案
分析:
基本方法
最简单的是枚举。枚举1e9很不现实,显然需要优化。
算法优化
算法优化1.1
先对枚举本身优化,减少枚举数量。经过数学分析我们很容易想到,5mod2和5mod4实际是一样的,9mod2,9mod4,9mod8也是一样的……别急着下结论,16mod3,16mod6,16mod12是一样的,但16mod9就不一样了。好像是这样,对于任意给定的a,任意m,得到的m*2^n(n belong to R+),余数相同。是这样吗?
算法优化1.1.1
反例很好举,比如,16mod24。于是,你还需要判断大小……枚举的优化需要复杂计算的时候,就不是很有效率了。
算法优化1.2
换个思路,哪些数一定会是余数?显然有了1.1.1中的反例,任意被除数a对b一定有余数a。而且,任意被除数a的最大余数也是a。从a向下枚举,举例a=17,余数可以是17,可以是16吗?显然不可能,同理15、14、13、12、11、10、9都不可能,而17mod9=8,17mod10=7……17mod16=1,17mod17=0。分界点在哪里呢?17的一半,8.5这里。 从0~8,加上17本身,都可以。
算法优化1.2.1
将这个集合的数量公式化。显然,0~8有九个数,是对17的一半向上取整(例:17/2+1)。
然后再+1(17本身)。得到a/2+2(a为整型变量)。
由于用到了整除操作,需要验证一下偶数,16显然为16/2+1,而不是16/2+2。
最简单的,可以分类讨论。
算法优化1.2.2
可以不分类讨论吗?当然可以。
主要以下思路:
1、 由于奇数要补1而偶数不用,给a先补上1,变成(a+1)/2+1;
2、 预处理。对奇数+1;
3、 利用a-a/2,得到向上取整的a÷2;
4、 利用向上取整函数floor(),将a/2改为a/2.0f(目的是使结果为浮点数),然后向上取整,再利用强制类型转换输出整型,或者利用流输出的控制只输出浮点数结果的整数部分。
请自行分析以上方法和分类讨论的效率。
1030: C基础-选择半径
题目描述:
编写一个程序,打印输出半径为1到10的圆的面积,若面积在40到90之间则予以打印,否则,不予打印。
1096.NEUQ的邮票
题目描述:
neuq最近推出了一套具有特殊意义的邮票,邮票的价值是从1分到N分。并且每张邮票的价值都不相同。
谷学长看中了这套邮票,可是一掏兜发现自己只有M分。但是他非常想买,于是他决定买总价值刚好为M分的邮票,但谷学长不希望他买的邮票断断续续,所以他还希望能够买从x分到y分连续的y-x+1张邮票。
你的任务是求出所有符合要求的方案,以[x,y]的形式输出。
输入:
输入包含多组数据,每组数据只有一行,包含两个数N和M(1<=N,M<=10^9)。
输出:
对于每组输入数据,每行输出包含一个合法方案:[x,y]。按x值从小到大输出。
每组输出数据不含任何空格。
这道题乍一看上去是一道很简单的等差数列求和的问题,但由于极为庞大的数据量,和1s的判题时间,让处理问题变得比较棘手。由于判题机一秒钟大约可以处理10^8多,不到10^9的数据量(看评测机的水平了),这道题的复杂度我们必须控制在O(n)以下,
首相我想到了,公式法,复杂度O(1),但这道题并不是一个输入一个输出的这种类型。。不可行。
第二:使用一次遍历,利用判断条件控制减少需要遍历的数据量,降低时间复杂度。
首先,我们需要先找到大致的O(n)的算法,分析一下已知的条件:已知等差数列的Sum(输入),等差数列的公差d=1,要求所有满足从x到y的和为Sum的方案。最普通的算法需要两个变量,首相和末相(或者首相和相数)才能求出这一段等差数列的Sum,如果这样的话我们就需要两个循环来遍历,复杂度达到了O(n^2),肯定超时。
如果我们只知道一个变量呢?
这时,需要用到求和公式了Sum=a1^n+n*(n-1)/2,用一重for循环遍历一个未知量,通过公式变形,求得另一个变量的表达式,判断该变量是否符合要求,即该变量是否为正整数,是否超过最大范围。如果符合要求,那么这组数据就是复合要求的,再通过转换求出末相输出即可。
这里初看是有两条路可以走的:
1.求出a1的表达式:a1=Sum/n+(n-1)/2,for循环遍历相数n,判断a1是否为正整数且a1<=N
2.求出相数n的表达式 :解一个关于n的二元一次方程,解得n=(1-2*a1+sqrt(4*a1*a1-4*a1+1+8*Sum))/2;判断n是否为正整数
由于2式关于n的表达式过于恶心,我选择第一种方法= =
好,我们终于将复杂度控制在O(n)了,接下来才是本题的重点:剪枝。 //找本题的隐藏条件。
这时用到了另一个求和公式:根据题目,Sum=(y-x+1)(y+x)/2,这里的(y-x+1)就代表相数,我们需要找的是关于相数的限制条件啊,得铆劲往相数上面凑,Sum是已知量,这里就得找(y-x+1)和(y+x)的关系了,而首相x>=1的,这就找到了(y-x+1)和(y+x)的不等关系:(y-x+1)<=(y+x),进而得到了相数的关键限制条件:(y-x+1)<=sqrt(2*sum)
ok,算法确定了,可以代码实现了 核心代码如下图:
ps:由于输入没有结束的标志,建议使用~scanf或者scanf()!=EOF
pps:求表达式判断是否为正整数这个方法经常用于减少时间复杂度
int m,sum;
while(~scanf("%d%d",&m,&sum))//邮票最大面额和钱的大小关系是不定的
{
for(int i=(int)(sqrt(2*sum)+0.5);i>=1;i--)//i代表项数,倒序循环是为了保证x从小到大输出
{
if((2*sum-(i-1)*i)%(2*i)==0 &&(2*sum-(i-1)*i)>0)//如果a1为整数,正数
{
//cout<<i<<endl;
if((2*sum-(i-1)*i)/(2*i)+i-1<=m && (2*sum-(i-1)*i)/(2*i)>0)//末相=末相<=邮票面额的最大值
{
cout<<'['<<(2*sum-(i-1)*i)/(2*i)<<','<<(2*sum-(i-1)*i)/(2*i)+i-1<<']'<<endl;
}
}
}
}
1133: C语言考试练习题_排列
题目描述:
有4个互不相同的数字,输出由其中三个不重复数字组成的排列。
输入:
4个整数。
输出:
所有排列。
1.先从四个数字内选三个数字,有三种选法
三种选法设置x,y,z进行三次赋值
2.每种选法有6种排列方式
6种排列先后顺序用x,y,z先后赋值
写一个三变量的函数,在主函数里调用6次,注意顺序。
1219: 哥德巴赫曾猜测
题目描述:
德国数学家哥德巴赫曾猜测:任何大于6的偶数都可以分解成两个素数(素数对)的和。但有些偶数可以分解成多种素数对的和,如: 10=3+7,10=5+5,即10可以分解成两种不同的素数对
输入:
输入任意的>6的正偶数(<32767)
输出:
试求给出的偶数可以分解成多少种不同的素数对(注: A+B与B+A认为是相同素数
方法一:
自定义一个判断是否为质数的函数,然后从令j为从2到n/2,遍历一遍,判断j和n-j是否都为质数,若是则计数器++
核心代码如下:
bool prime(int x)
{
for(int i=2;i<=sqrt(x+0.5);i++)
if(x%i==0)
return false;
return true;
}
int n;
scanf("%d",&n);
int cnt=0;
for(int j=2;j<=n/2;j++)
{
if(prime(j)&&prime(n-j))
cnt++;
}
printf("%d",cnt);
方法二:(选看)
先找出32767内的所有质数,将其存入vector中,然后再令j为从2到n/2,判断j和n-j是否都在vector中,如果都在则计数器++。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
#define Shift 5
using namespace std;
const int N=32800;
const int M=N>>Shift;
unsigned int a[M+1];
vector<int> prime;
vector<int>::iterator it;
vector<int>::iterator tt;
void setbit(int x)
{
a[x>>Shift]|=1<<(x&((1<<Shift)-1));
}
bool getbit(int x)
{
return a[x>>Shift]&(1<<(x&((1<<Shift)-1)));
}
void init()
{
memset(a,true,sizeof(a));
for(int i=2;i<=N;i++)
{
if(!getbit(i))
prime.push_back(i);
for(int j=0;j<prime.size()&&i*prime[j]<=N;j++)
{
setbit(i*prime[j]);
if(!(i%prime[j]))
break;
}
}
}
int main()
{
init();
int n;
scanf("%d",&n);
int cnt=0;
for(int j=2;j<n/2+1;j++)
{
it=find(prime.begin(),prime.end(),j);
tt=find(prime.begin(),prime.end(),n-j);
if(it!=prime.end()&&tt!=prime.end())
cnt++;
}
printf("%d",cnt);
return 0;
}
1238: 换位置
题目描述:
M个人围成一圈,每分钟相邻的两个人可以交换位置(只能有一对交换)。求使M个人的顺序颠倒(即每个人左边相邻的人换到右边,右边相邻的人换到左边)所需的最少时间(分钟数)。
输入:
第一行为测试数据的组数n,以后n行中每行为一个小于32767的正整数,表示M
输出:
对于每组测试数据,输出一个数,表示最少需要的分钟数。
简单的找规律问题
当m为偶数时,可以将一个圈分成左右两半分别计算,两边移动时间是相同的,只需将把一半全部倒置所需的时间乘2即可,最后推出(m/2)*(m/2-1);
当m为奇数时,同理,也容易推出(m-1)/2*(m-1)/2。
核心代码如下:
int n,m; scanf("%d",&n); while(n--) { scanf("%d",&m); if(m%2==0) printf("%d\n",(m/2)*(m/2-1)); //m为偶数时; else printf("%d\n",(m-1)/2*(m-1)/2); //m为奇数时; }
1249: 日期排序
题目描述:
有一些日期,日期格式为“MM/DD/YYYY”。编程将其按日期大小排列。
bool compare(date a,date b)//用sort函数必须建一个compare.
{
if(a.year==b.year)
{
if(a.month==b.month)
{
return a.day<b.day;
}
else
{
return a.month<b.month;
}
}
else
{
return a.year<b.year;
}
}
sort(a,a+cnt,compare);
1161: 【C语言训练】百钱百鸡问题
中国古代数学家张丘建在他的《算经》中提出了著名的“百钱买百鸡问题”:鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?
1288: 青年歌手大奖赛_评委会打分
题目描述:
青年歌手大奖赛中,评委会给参赛选手打分。选手得分规则为去掉一个最高分和一个最低分,然后计算平均得分,请编程输出某选手的得分。
输入:
输入数据有多组,每组占一行,每行的第一个数是n(2<n<100),表示评委的人数,然后是n个评委的打分。
输出:
对于每组输入数据,输出选手的得分,结果保留2位小数,每组输出占一行。
分析:
基本方法
定义一个100以上的数组,初始化为0,将原始成绩存入数组,然后将数组遍历,将最大最小数变为0,求数组和,除以(n-2)。
算法优化
上述方法实际上遍历了两次数组。如果遍历时同时:
1、 记录已经出现的最大值;
2、 记录已经出现的最小值;
3、 记录已经出现数的和。
最后将和减去最大最小值,除(n-2)。
那么只需用一个for循环。当然这对时间的减少有限而且有条件(n较小时可能会更费时),但是如果题目允许n非常大,而且给定运行内存很小的情况下,这种方法根本不需要开数组(读一个数处理一个),能减少内存占用。