蒟蒻洛谷奇遇记

 P1002过河卒

c9028546da444a4ea6fb3a552de2e51a.png

分析题目,是马拦卒,那么在中国象棋中,马是日字走法,这意味着这道题里马能控制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 铺地毯

6756706382d441d184b389b7a74814a1.png

分析题目,答案就是判断点最上面的地毯号

第一想法就是像刷油漆一样把第一个地毯全部点刷上"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独木桥

c95b7bbc3fcf4460b753fdb216ee4008.png

分析题目 字虽然挺长,但解题思路却不难找到,因为每个士兵的方向是可以随意定义的

     那么                    最短的时间 =        最靠近独木桥中间的人撤离的时间

                              最长的时间 = 最远离独木桥中间的人跨越这个独木桥撤离的时间

第一想法是用数组去存储每一个人的撤离时间,但这样反而让解决变得更加复杂,样本量的增多也增加了时间的复杂度。所以干脆就读取一个士兵的坐标就来比较一次大小,抹除了数组的复杂度,也让代码更加简洁易懂。

代码如下:

#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 三连击

d0331e98791f4c088670a0a40d0c6afb.png

分析题意 主要难点是要实现三个三位数是由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 ;//直接输出分子分母即可
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaozi_512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值