P1002过河卒
分析题目,是马拦卒,那么在中国象棋中,马是日字走法,这意味着这道题里马能控制9个点(包括起始点)而卒要到指定的点只能从点B的上方和左边到达
那么:) 到B点(x,y)的路径数= 到( x,y-1)路径数 + 到(x-1,y)路径数 ;
从这里出发,两个循环递归出卒的位置变化,并利用bool数组来判断是否经过马的控制范围
代码如下:
#include<stdio.h>
#include<stdbool.h>
const int x[] = {0,-2,-2,2,2,-1,-1,1,1};
const int y[] = {0,-1,1,-1,1,2,-2,2,-2};//马的位置(日字走法)
long long f[40][40];//卒的位置
bool s[40][40];//判断是不是马所在的位置,是就为1
int main(){
int bx,by,mx,my;//Bx By 马x 马y
scanf("%d %d %d %d",&bx,&by,&mx,&my);
bx += 2;by += 2;mx += 2;my += 2;//因为马的位置变化有'-2',防止越界 就让起点(0,0)变成(2,2)但是路线的数量没有发生改变
s[mx][my] = 1;
for(int i = 1;i <= 8;i++)s[mx + x[i]][my + y[i]] = 1;//让马可能的所有的位置为1
f[2][1] = 1;//(2,2)到(2,1)只有一条
for(int i = 2;i <= bx ;i++){
for(int j = 2;j<= by;j++){
if(s[i][j])continue;
f[i][j] = f[i-1][j] + f[i][j-1];
}
}
printf("%lld\n",f[bx][by]);
return 0;
}
P1003 铺地毯
分析题目,答案就是判断点最上面的地毯号。
第一想法就是像刷油漆一样把第一个地毯全部点刷上"1",第二个地毯刷上"2",遇到同样的区域直接覆盖,以此类推,并用数组存储起来,最后根据点来输出数组中存储的值,可惜遇到不会解决的“ Sagementation Fault ”
于是转变思路,判断所给的坐标是不是在地毯里即可,然后输出该点最上面的地毯值即可
代码如下:
#include<stdio.h>
long long f[100000][40];
long long g[100000][40];//开大点总没问题 防止接收太多数据而RE(但记得longlong
int main(){
int n,flag=1;//flag意味着第几号地毯
scanf("%d",&n);
while(flag<=n){
int x,y,l,w;//(x,y),length,width
scanf("%d %d %d %d",&x,&y,&l,&w);
f[flag][1] = x;f[flag][2] = y;//左下角
g[flag][1] = x+l;g[flag][2] = y+w;//右上角
flag++;
}
int a,b;
scanf("%d %d",&a,&b);
for(int i = n;i>0;i--){
if(a>=f[i][1] && b>=f[i][2] && a<=g[i][1] && b<=g[i][2]){//判断点在不在地毯里,从上往下看
printf("%lld",i);
return 0;
}
}
printf("-1");
return 0;
}
P1004 方格取数
分析题目: 找到2条路劲上取和最大,那么同时出发是最省时间的方法,那就会有四种情况:
1)都往下 2)一往右 二往下 3)一往下 二往右 4)都往右
根据四种情况写一个函数来比较哪种路径和最大即可
代码如下:
#include<iostream>
using namespace std;
int N = 0;
int f[100][100][100][100];
int s[100][100];
int max(int a,int b){
return a>b?a:b;
}
int result(int x,int y,int x1,int y1){
int M = 0;
if(f[x][y][x1][y1] != -1)return f[x][y][x1][y1];//如果这种情况被记录 就直接返回
if(x == N && y == N && x1 == N && y1 == N)return 0;
if(x < N && x1 < N)M = max(M,result(x+1,y,x1+1,y1+1)+s[x+1][y]+s[x1+1][y1]-s[x+1][y]*(x1+1==x+1&&y==y1));//往下走
if(x < N && y1 < N)M = max(M,result(x+1,y,x1,y1+1)+s[x+1][y]+s[x1][y1+1]-s[x+1][y]*(x1==x+1&&y==y1+1));//第一种方案往下,第二种往右
if(y < N && x1 < N)M = max(M,result(x,y+1,x1+1,y1)+s[x][y+1]+s[x+1][y]-s[x][y+1]*(x1+1==x&&y+1==y1));//第一种方案往右,第二种往下
if(y < N && x1 < N)M = max(M,result(x,y+1,x1,y1+1)+s[x][y+1]+s[x1][y1+1]-s[x][y+1]*(x1==x&&y+1==y1+1));//往右走
f[x][y][x1][y1] = M;
return M;
}
int main(){
cin >> N;
for(int a=0;a<=N;a++)
for(int b=0;b<=N;b++)
for(int c=0;c<=N;c++)
for(int d=0;d<=N;d++)
f[a][b][c][d]=-1;//重置f数组
while(1){//读入
int a1 = 0,b1 = 0,c1 = 0;
cin >> a1 >> b1 >> c1;
if(a1 == 0 && b1 == 0 && c1 == 0)break;
s[a1][b1] = c1;
}
cout << result(1,1,1,1) + s[1][1];//包含上起始点
return 0;
}
P1006传纸条
分析题目 本题和方格取数类似,但我用了三维数组来处理,没有选择过于繁琐的四维数组,思路大概一致,找两条路径所求和最大,但多了个不能有重复的方格,需要进行查重
代码如下:
#include<iostream>
using namespace std;
int N;
int a[100][100][100];//a[步数][1的y][2的y]
int s[100][100];//好心值
int max(int a,int b){
return a>b?a:b;
}
int main(){
int n,m;
cin >> n >> m;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
cin >> s[i][j];
a[1][1][1] = s[1][1];//初始话出发点的好心值为0
for(int step = 2;step <= n+m-1;step++)
for(int i = 1;i <= step && i <= n;i++)
for(int j = 1;j <= step && j <= n;j++){
if(i ==1 && j == 1)continue;
//四种情况的比较,看哪种情况的好心值最大
a[step][i][j] = max(max(a[step-1][i][j],a[step-1][i-1][j-1]),max(a[step-1][i][j-1],a[step-1][i-1][j]));
a[step][i][j] += i==j?s[i][step-i+1]:s[i][step-i+1]+s[j][step-j+1];
//如果两条路径选择了同一个同学,那么只加一次好心值
}
cout << a[m+n-1][n][n];//输出到达终点的好心值累和
return 0;
}
P1007独木桥
分析题目 字虽然挺长,但解题思路却不难找到,因为每个士兵的方向是可以随意定义的
那么 最短的时间 = 最靠近独木桥中间的人撤离的时间
最长的时间 = 最远离独木桥中间的人跨越这个独木桥撤离的时间
第一想法是用数组去存储每一个人的撤离时间,但这样反而让解决变得更加复杂,样本量的增多也增加了时间的复杂度。所以干脆就读取一个士兵的坐标就来比较一次大小,抹除了数组的复杂度,也让代码更加简洁易懂。
代码如下:
#include<stdio.h>
int min(int a,int b);
int max(int a,int b);//因为c语言中没有'algorithm.h' 所以写了两个函数来判断大小
int main(){
int l,n,x=0;
int a,b,a1=0,b1=0;//记得初始化a1,b1的值
scanf("%d",&l);
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d",&x);
a = min(x,(l+1)-x);
b = max(x,(l+1)-x);
a1 = max(a,a1);
b1 = max(b,b1); //交替比较大小,最后得出时间
}
printf("%d %d",a1,b1);
return 0;
}
int min(int a,int b){
return a<b?a:b;
}
int max(int a,int b){
return a>b?a:b;//a大于b? 大于return a 反之return b (大概意思上同)
}
P1008 三连击
分析题意 主要难点是要实现三个三位数是由1-9分成三组,且没有重复数字。
好在样本量少,复杂度低,直接for循环递增,然后if判断条件即可
代码如下:
#include<stdio.h>
int sum(int n);
int fac(int n);//两个函数来判断三个三位数是否符合题意
int main(){
int a,b,c;
for(int a = 123;a <= 333;a++){
b = 2*a;
c = 3*a;
if(sum(a)+sum(b)+sum(c)==1+2+3+4+5+6+7+8+9 &&fac(a)*fac(b)*fac(c)==1*2*3*4*5*6*7*8*9){
printf("%d %d %d\n",a,b,c);
}
}
return 0;
}
int sum(int n){
int sum=0;
while(n>0){
sum+=n%10;
n=n/10;
}
return sum;
}
int fac(int n){
int fac=1;
while(n>0){
fac*=n%10;
n=n/10;
}
return fac;
}
P1009 阶乘之和
分析题目 虽然题目很短 但是我认为比上面的题都难,主要是处理这个高精度问题
利用数组去存储1~n的阶乘,再用数组去将他们加起来
高精度的加法主要参考竖式计算,每一位同每一位相加,然后处理进位机制即可
代码如下:
#include<stdio.h>
int main(){
int n,c=0,d=0;
scanf("%d",&n);
int sum[100]={0},a[100]={1};//定义一个比较长的数组,防止数据越界
for(int i = 1;i <= n;i++)
for(int j = 0;j<100;j++){
a[j] = a[j]*i + c;
c = a[j] /10;
a[j] = a[j] %10;//存入每一项的阶乘
sum[j] += a[j] + d;
d = sum[j] / 10;//进位处理,上同
sum[j] = sum[j] % 10;//阶乘的和
}
int x;
for(x = 99;;x--){
if(sum[x] != 0){
break;//除去结果里不到100位数后面多余的'0'
}
}
for(int i = x;i >= 0;i--){
printf("%d",sum[i]);//倒序输出
}
return 0;
}
P1010幂次方
分析题目,n最大取20000,因此最多是2^14,我第一想法简单暴力的用switch case直接输出完事了
代码如下:
#include<stdio.h>
#include<math.h>//pow(x,y)需要调用math库
int main(){
int n,tot=1,flag=0;
scanf("%d",&n);
for(int i =0 ;n > 0;i++){
tot=pow(2,i);
if(tot > n){
i=i-1;
if(flag == 1){
printf("+");//判断要不要加'+'
}
n=n-pow(2,i);
flag=1;
turn(i);
i=0;//重置i的值
continue;//防止i+1,如果这里没有也可以 但上面i要变-1
}
}
return 0;
}
void turn(int n){//简单粗暴,注意不要搞错了 奇数是偶数加一个2(0)
switch(n){
case 14:printf("2(2(2+2(0))+2(2)+2)");break;//最多取14
case 13:printf("2(2(2+2(0))+2(2)+2(0))");break;
case 12:printf("2(2(2+2(0))+2(2))");break;
case 11:printf("2(2(2+2(0)+2(2+2(0)))");break;
case 10:printf("2(2(2+2(0))+2)");break;
case 9:printf("2(2(2+2(0)+2(0))");break;
case 8:printf("2(2(2+2(0)))");break;
case 7:printf("2(2(2)+2+2(0))");break;
case 6:printf("2(2(2)+2)");break;
case 5:printf("2(2(2)+2(0))");break;
case 4:printf("2(2(2))");break;
case 3:printf("2(2+2(0))");break;
case 2:printf("2(2)");break;
case 1:printf("2");break;
case 0:printf("2(0)");break;
}
return 0;
}
但如果题目的数字再给大一点,这个显然不太合适,过长也过于繁琐。
因此重新写了一个,这次的可以处理12位数的任意数字
代码如下:
#include<stdio.h>
#include<math.h>//pow(x,y)需要调用math库
int main(){
int n;
scanf("%d",&n);
turn(n);
return 0;
}
void turn(int n){
for(int i=0;n>0;i++){
if(pow(2,i) > n){
i -= 1;
n -= pow(2,i);
if(i==1){
printf("2");
}
else if(i==0){
printf("2(0)");
}
else{
printf("2(");
turn(i);//递归
printf(")");
}
if(n != 0){
printf("+");
}
i = -1;//让i重置会0,设为-1是因为循环一遍后会加1
}
}
}
P1011车站
分析题目,本题的重点是第二站是上下车的人数是未知的,因此利用for循环来找到这个值,进而推算出,到目标车站时的人数
代码如下:
#include<stdio.h>
int q[100],e[100];//两个数组,一个是上车一个是下车
int main(){
int a,n,m,x,st;
int sum[100];//每一站时候的总人数
scanf("%d %d %d %d",&a,&n,&m,&x);
q[1] = a;//首发站(第一站)
for(st = 0;;st++){
sum[1] = a;
q[2] = st;
sum[2] = a;//初始化数组
for(int i=3;i < n;i++){
q[i] = q[i-1] + q[i-2];
e[i] = q[i-1];
sum[i] = sum[i-1] + q[i] - e[i];//前一站的人数加上本站的人数变化量就是本站人数
}
if(sum[n-1] == m){
break;//注意最后一站不上人,也就是说前一站出发是的人数就是最后一站的下车人数
}
}
printf("%d",sum[x]);
return 0;
}
P1012拼数
分析题目,本质是个排序题,但不是简单的冒泡排序,而是组成一个最大数字的排序,本题的关键所在是做出最大的数字,在长度一定的情况下,自然第一个数字越大越好。因此,利用for循环对数据进行整理排序,对前后两个数据进行比较,交换来达到本题的目的
代码如下:
#include<stdio.h>
#include<math.h>
int len(int n);//计算数字长度的函数
int compare(int n,int m);//进行前后数据比较的函数
void swap(int *pa,int *pb);//交换数据的函数
int a[100];//读入数据和输出整理后数据的数组
int b[2][100];//用于比较的数组
int main(){
int n;
char ch;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);//读入数据
ch = getchar();//吃空格
}
for(int i=0;i < n-1;i++){
for(int j=i+1;j < n;j++){
if(compare(a[i],a[j]) == 1){//排序问题,在所给条件下对数据进行排序
swap(&a[j],&a[i]);//交换
}
}
}
for(int i=0;i < n;i++){
printf("%d",a[i]);//输出数据
}
return 0;
}
int len(int n){
int i=0;
while(n > 0){
n = n/10;
i++;
}
return i;
}
int compare(int n,int m){
int i = len(n) - 1;
int q = n,e = m;
while(n>0){//正向读入 n+m;
b[0][i] = n % 10;
n /= 10;
i--;
}
i = len(q) + len(e) - 1;
while(m>0){
b[0][i] = m %10;
m /= 10;
i--;
}
n = q,m = e;//注意在循环中n和m的损耗
i = len(m) - 1;
while(e>0){//反向读入 m+n
b[1][i] = e % 10;
e /= 10;
i--;
}
i = len(n) + len(m) - 1;
while(q>0){
b[1][i] = q %10;
q /= 10;
i--;
}
for(int x = 0;x < len(n)+len(m);x++){//比较
if(b[1][x] > b[0][x]){
return 1;
}
else if(b[1][x] == b[0][x]){
continue;
}
return 0;
}
return 0;
}
void swap(int *pa,int *pb){
int c = *pa;
*pa = *pb;
*pb = c;
}
做这道题的时候,第一想法是就通过第一个数字大小进行排序,但忽略了相同大小的数字如何处理,因此转变成用数组来进行比较。在做完这道题后,翻看题解,发现了在c++中可以用字符串来直接比较大小,虽然和我的基本原理相似,但其代码简单了许多。我也尝试在纯c语言中试着用字符串来进行比较,但是没能达到预期的效果,等我学到了c++,我再回来把代码补齐:)
P1014 Cantor 表
分析题目,这个题比较简单,就是要分奇偶来判断分母分子谁来加减,判断正确本题答案就呼之欲出了
代码如下:
#include<iostream>
#include<string>
using namespace std;
int main(){
int n;
cin >> n;//读入要得出的第n项
int flag = -1;//判断奇偶的标志
int sta1 = 1,sta2 = 1;//分子 与 分母
for(int i = 1;i < n;){
if(flag == 1 ){//偶 则分母递减,分子递增
sta1 += 1;
i++;
while(sta1 > 1 && i !=n){
sta1--;
sta2++;
i++;
}
}
else if(flag == -1 ){//奇,则分子递减,分母递增
sta2 += 1;
i++;
while(sta2 > 1 && i !=n){
sta2--;
sta1++;
i++;
}
}
flag = -flag;//奇偶变化
}
cout << sta1 << "/" << sta2 ;//直接输出分子分母即可
}