洛谷刷题(一)
P1007独木桥
题目背景
战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 11 个人通过。假如有 22 个人相向而行在桥上相遇,那么他们 22 个人将无法绕过对方,只能有 11 个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。
题目描述
突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为 LL,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 11,但一个士兵某一时刻来到了坐标为 00 或 L+1L+1 的位置,他就离开了独木桥。
每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。
由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。
输入格式
第一行:一个整数 LL,表示独木桥的长度。桥上的坐标为 1\cdots L1⋯L。
第二行:一个整数 NN,表示初始时留在桥上的士兵数目。
第三行:有 NN 个整数,分别表示每个士兵的初始坐标。
输出格式
只有一行,输出 22 个整数,分别表示部队撤离独木桥的最小时间和最大时间。22 个整数由一个空格符分开。
输入输出样例
输入 #1/span>4 2 1 3输出 #12 4说明/提示
初始时,没有两个士兵同在一个坐标。
数据范围 1=<L<=5000 0<N<=5000,保证N<L;
此题分别要求求出最长和最短时间,下面分别分析
最短时间:因为题目已经说明最初没有位置有两个人,所以只要每个人都向离自己最近的方向走,就可以做到最快撤离,最短时间为按这种方案撤离需要时间最长的人
最长时间:按照最短时间的方式思考,自然而然的得出当所有人都像离自己远的一端移动时,时间最长,但是当桥两边的士兵相向运动时,会出现许多的变化情况。如下图
但由于所有相向运动最终都会回归为各自背向运动,所以最初的思路时 最长时间=达到背向运动的时间+达到背向运动状态时位置的最短时间
但是在实际编码过程中发现,求出状态变化过程的时间太过复杂。所以换了一种思路。以上的想法是把每个士兵看作有差异的个体,但是实际上我们需要的只是所有的士兵成功撤离,并不关心具体的某位士兵撤离到了那一边,
所以可以忽略相遇转向的过程,直接视为可以互相穿过
eg:最初的想法是这样的
但是忽略士兵的不同之后可以看作
所以可以直接比较每位士兵到达较远端的时间,最大的一个即最长时间
以下是代码
#include<bits/stdc++.h> using namespace std; int main() { int L,N,tmin=0,tmax=0; cin>>L; cin>>N; for(int i=0;i<N;i++) { int place; cin>>place; tmin=max(tmin,min(place,L+1-place)); tmax=max(tmax,max(place,L+1-place)); } cout<<tmin<<' '<<tmax; }
P1008 [NOIP1998 普及组] 三连击
题目背景
本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。
题目描述
将 1, 2, \ldots , 91,2,…,9 共 99 个数分成 33 组,分别组成 33 个三位数,且使这 33 个三位数构成 1 : 2 : 31:2:3 的比例,试求出所有满足条件的 33 个三位数。
输入格式
无
输出格式
若干行,每行 33 个数字。按照每行第 11 个数字升序排列。
输入输出样例
输入 #1无
输出 #1192 384 576 * * * ... * * * (剩余部分不予展示)
分析
最初看到本题,可能下意识是从分组的情况去思考,即先分组再比较值这一较为简单的思考方法,但是实际实行时发现如果先分组,只是进行随机分为三组的操作就已经极为复杂,而每一组的取值有需要经过循环来取得,最终还需要匹配各组产生的值,
难免让人感觉吃力不讨好。所以不如换一种思路,先取出所有三位数字各不相同的三位数,分别取其2倍和3倍,然后看这两个数的数字是否已经使用过,思路如此,以下时代码
#include<bits/stdc++.h> using namespace std; int number[10];//标记1---9的数字是否使用过。 int isok(int num) { //分解数字 int a,b,c; a=num/100; b=(num%100)/10; c=num%10; //判断是否使用过。 if(number[a]!=1&&number[b]!=1&&number[c]!=1) { number[a]=1; number[b]=1; number[c]=1; return 1; } else return 0; } int main() { int num1,num2,num3; int i,j,z; for(i=1;i<=9;i++) { for(j=1;j<=9;j++) { if(j!=i) for(z=1;z<=9;z++) { if(z!=i&&z!=j)//保证三位数字不同 { //将每个使用过的数字标记 number[i]=1; number[j]=1; number[z]=1; num1=i*100+j*10+z; num2=num1*2; num3=num1*3; //排除2倍或3倍不是三位数的情况 if(num2>=1000||num3>=1000) { continue; } //判断2倍和3倍的结果的数字是否使用过 if(isok(num2)&&isok(num3)) { printf("%d %d %d\n",num1,num2,num3); } //当某一个num已经被验证之后将标记数组归0,进行下一次循环。 for(int l=1;l<10;l++) { number[l]=0; } } } } } }
P1009 [NOIP1998 普及组] 阶乘之和
题目描述
用高精度计算出 S = 1! + 2! + 3! +.......... + n!(1<=n≤50)。
其中“!”表示阶乘,例如:5! = 5*4*3*2*1
输入格式
一个正整数 nn。
输出格式
一个正整数 SS,表示计算结果。
输入输出样例
输入 #13输出 #19说明/提示
【数据范围】
对于 100 \%100% 的数据,1<=n<=50
此题是典型的高精度计算,需要用到的是高精度乘法和高精度加法
具体操作见高精度算法(乘法),加法
此处讲一些操作中的注意事项
- 由于进位的问题,建议所有的数据在最终输出之前,反向储存在数组中,防止如果最高位需要进位的话,需要移动整个数组。
- 进位的过程中 if(a[i]>=10){a[i+1]+=a[i]/10,a[i]=a[i]%10}中判断语句中的‘=’不能忘记,否则容易出现最后相加时发现数组溢出
- 最后如果是算法题,建议直接开出几个足够大的数组,可以极大的加快编码速度(自己编码的时候没想到可以直接开大数组,选择了直接用vector<int>类来进行动态分配,感觉算法很明显但是编码过程用了很久)O(≧口≦)O
下面是我的代码(看过题解总感觉自己写成这样很蠢 ,可恶!!!!!(〃>目<) )、
#include<iostream> #include<vector> #include<cmath> using namespace std; vector<int> getSum(vector<int>a, vector<int>b); void getSumfactorial(int n, vector<int>* result); vector<int> getfactorial(int n); vector<int> getproduct(vector<int> result, int num); vector<int> getSum(vector<int>a, vector<int>b); int main() { int n; vector<int> result; cin >> n; getSumfactorial(n, &result); for (int i = 0; i < result.size(); i++) { cout << result[i]; } cout << endl; return 0; } //求阶乘和 void getSumfactorial(int n, vector<int>* result) { int i; vector<int>a; vector<int>b; b.push_back(0); for (i = 1; i <= n; i++) { a = getfactorial(i); b = getSum(a, b); } for (i = b.size() - 1; i >= 0; i--) { (*result).push_back(b[i]); } if ((*result).back() >= 10) { (*result).push_back(0); } for (i = 0; i < result->size(); i++) { if ((*result)[i] >= 10) { (*result)[i + 1] += (*result)[i] / 10; (*result)[i] = (*result)[i] % 10; } } } //求阶乘 vector<int> getfactorial(int n) { vector<int> result; int i; result.push_back(1); for (i = 1; i <= n; i++) { result = getproduct(result, i); } return result; } //将整数转化为数组 void changetovector(int num, vector<int>* cs) { while (num / 10 > 0) { cs->push_back(num % 10); num = num / 10; } cs->push_back(num); } // 高精乘法 vector<int> getproduct(vector<int> result, int num) { vector<int> cs;//乘数 changetovector(num, &cs); int L1 = result.size(); int L2 = cs.size(); int Lc; if (result[L1 - 1] * cs[L2 - 1] >= 10) { Lc = L1 + L2; } else Lc = L1 + L2 - 1; int* c; c = new int[Lc]; for (int i = 0; i < Lc; i++) { c[i] = 0; } for (int i = 0; i < L1; i++) { for (int j = 0; j < L2; j++) c[i + j] += result[i] * cs[j]; } for (int i = 0; i < Lc - 1; i++) { if (c[i] >= 10) { c[i + 1] += c[i] / 10; c[i] = c[i] % 10; } } result.clear(); for (int i = 0; i < Lc; i++) { result.push_back(c[i]); } return result; } vector<int> getSum(vector<int>a, vector<int>b) { int i; vector<int> res; int La = a.size(); int Lb = b.size(); if (La < Lb) { for (i = La; i < Lb; i++) { a.push_back(0); } } if (Lb < La) { for (i = Lb; i < La; i++) { b.push_back(0); } } int bigger = max(La, Lb); for (i = 0; i < bigger; i++) { if (i < res.size()) res[i] = a[i] + b[i]; else res.push_back(a[i] + b[i]); } if (res.back() >= 10) { res.push_back(0); } for (i = 0; i < res.size(); i++) { if (res[i] >= 10) { res[i + 1] += res[i] / 10; res[i] = res[i] % 10; } } return res; }
感觉还可以利用存储状态的方法优化以下求阶乘的过程,不需要每个阶乘都重复求,但是可能要等之后有空才能尝试了 ┑( ̄Д  ̄)┍
最后贴以下题解大佬的代码,○| ̄|_
#include<stdio.h> int main() { int i,A[1005]={0},B[1005]={0},n,j; scanf("%d", &n); A[0]=B[0]=1; for (i=2;i<=n;i++){ for (j=0;j<100;j++) B[j]*=i; for (j=0;j<100;j++) if (B[j]>9){ B[j+1] += B[j]/10; B[j]%=10; } for (j=0;j<100;j++){ A[j]+=B[j]; if (A[j]>9) { A[j+1] += A[j]/10; A[j]%=10; } } } for (i=100;i>=0&&A[i]==0;i--); for (j=i;j>=0;j--) printf("%d", A[j]); return 0; }