2019爪哇小白校内热身赛(个人赛)

A - A + B(模拟 签到)


原题链接: HDU 1228 A + B

思路1:map

用map容器把字符串与相应的数字依次对应起来,用两个while循环,第一个while循环读到 ‘+’ 号则结束,即确定第一个整数的值;第二个while循环读到 ‘=’ 号则结束,即确定第二个整数的值,最后把两个整数的值相加起来。

注意num1和num2要初始化为0,否则会导致输出的数很大很大,敲黑板!!!

推荐一篇写得很好的博客:map的详细用法

代码如下:

#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
int main()
{
	map<string,int> m;	//映射 
	m["zero"]=0;
    m["one"]=1;
    m["two"]=2;
    m["three"]=3;
    m["four"]=4;
    m["five"]=5;
    m["six"]=6;
    m["seven"]=7;
    m["eight"]=8;
    m["nine"]=9;
    m["+"]=10;
    m["="]=11;
    while(1)
    {
    	int num1=0,num2=0,ans;
    	string str;
    	while(cin>>str)	//依次输入字符串,遇到空格会结束此次输入 
    	{
    		if(m[str]==10)	//遇到'+'号则退出循环 
    			break;
    		num1=num1*10+m[str];	
    	}
    	while(cin>>str)
    	{
    		if(m[str]==11)	//遇到'='号则退出循环 
    			break;
    		num2=num2*10+m[str];
    	}
    	if(num1==0&&num2==0)
    		break;
    	ans=num1+num2;	//相加 
    	printf("%d\n",ans);
    }
}

思路2:字符串处理

用字符串数组把字符串与数字一一对应起来,可以多看几遍,加深对字符串数组运用的理解。

不懂strcmp()函数的看这里:C++标准库字符串函数总结

代码如下:

#include<cstdio>
#include<cstring>
using namespace std; 
char a[10][10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
int change(char s[])	//字符串数组用法
{
    for(int i = 0; i < 10; i++)
    {
    	if(!strcmp(a[i], s))	//字符串相等 
            return i;
    }
}
int main()
{
    char str[10], num[10];
    while(1)
	{
        int a = 0, b = 0;
        while(scanf("%s", str) && strcmp(str, "+"))	//注意字符串数组的用法 
            a = a*10+change(str);
        while(scanf("%s", str) && strcmp(str, "="))
            b = b*10+change(str);
        if(!(a+b))  //和为0,即a和b均为0,跳出循环 
			break;
        printf("%d\n", a+b);
    }
    return 0;
}


B - 3n + 1?(模拟 签到)


原题链接: POJ 1207 The 3n + 1 problem

思路:

1、左区间可以大于右区间,不影响,坑!!!
2、不需要单独考虑0和1的情况。
3、初始操作数应为1或者说最后一步判定为1时要操作数要+1。

代码1:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int main()
{
	int l,r;
	while (cin>>l>>r)                    
	{
		int m=min(l,r);	//不能写成l=min(l,r),这样就影响最大值的判断了 
		int n=max(l,r);
		int num=0,total,t;
		for (int i=m;i<=n;i++)
		{
			t=i;
			total=0;	//计算每个数的操作数之前记得初始化为0 
			while(t!=1)
			{
				if (t&1)                 // 判断为奇数
				{
					t=t*3+1;   
					total++;
				} 
				else if(t^0)             // 判断为偶数
				{
					t=t/2;
					total++;
				}
			}
			num=max(num,total);	         
		}
		cout<<l<<" "<<r<<" "<<num+1<<endl;	//注意结果要+1 
	}
	return 0;
}

代码2:

比赛时的写法,想得比较细,把0和1的情况也考虑了,但其实不需要,代码太冗长了。(不建议采用)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; 
int main() 
{
	int l,r;
	while(scanf("%d%d",&l,&r)!=EOF)
	{
		int ans=0;
		if(l>r)	//右区间大于左区间的情况 
		{
			cout<<l<<" "<<r<<" ";	//先输出区间边界 
			if(r==0&&l==1)	//区间[1,0]的情况0种 
			{
				cout<<0<<endl;
			} 
			else if(r==0&&l!=1)	//右区间为0,左区间不为0的情况 
			{
				for(int i=r+1;i<=l;i++)
				{
					int t=i;	//暂存 
					int total=0;
					while(t!=1)	//不为1则循环 
					{
						if(t%2==0)	//偶数时 
						{
							t/=2;
							total++;
						}
						else	//奇数时 
						{
							t=t*3+1;
							total++;
						}
					}
					ans=max(ans,total);	//找出最大操作数 
					
				}
				cout<<ans+1<<endl;	//记得加上1!!! 
			}
			else	//左区间不为0,右区间不为1的情况 
			{
				for(int i=r;i<=l;i++)
				{
					int t=i;
					int total=0;
					while(t!=1)
					{
						if(t%2==0)
						{
							t/=2;
							total++;
						}
						else
						{
							t=t*3+1;
							total++;
						}
					}
					ans=max(ans,total);
					
				}
				cout<<ans+1<<endl;
			}
			
		} 
		else	//右区间大于左区间的情况 
		{
			if(l==0&&r!=1)	//左区间为0,右区间不为0的情况 
			{
				l++;	//左区间边界变为1 
				for(int i=l;i<=r;i++)
				{
					int t=i;
					int total=0;	//记得每个数的操作数开始前先初始化为0 
					while(t!=1)
					{
						if(t%2==0)
						{
							t/=2;
							total++;
						}
						else
						{
							t=t*3+1;
							total++;
						}
					}
					ans=max(ans,total);
					
				}
				cout<<0<<" "<<r<<" "<<ans+1<<endl;
			}	
			else if(l==0&&r==1)	//区间[0,1]的情况 
			{
				cout<<0<<" "<<1<<" "<<0<<endl;
			}
			else	//左区间不为0,右区间不为1的情况 
			{
				for(int i=l;i<=r;i++)
				{
					int t=i;
					int total=0;
					while(t!=1)
					{
						if(t%2==0)
						{
							t/=2;
							total++;
						}
						else
						{
							t=t*3+1;
							total++;
						}
					}
					ans=max(ans,total);
					
				}
				cout<<l<<" "<<r<<" "<<ans+1<<endl;
			}
		} 
		
	}
	return 0;
	
}


D - 序列和(贪心 签到)


原题链接: HDU 1231 最大连续子序列

思路:

  1. 遍历序列,累加序列的数,如果序列和小于等于0,则从下一位重新累加,同时暂存起始点,如果大于0,则继续累加。
  2. 在每次循环里,比较子序列和最大值和当前序列和,若当前序列和大于最大值,则重新记录最大值、起始点和终止点。
  3. 注意,测试样例里有组全为负数的例子,此时序列和为0,起始点终止点就是序列起始点终止点。所以用一个计数器记录负数数量,若等于序列数目,则为该情况。
  4. 本场比赛第二次因为计数器没有初始化为0而导致WA,再次敲黑板!!!

代码如下:

#include <iostream>
#include <cstdio>
using namespace std; 
int main() 
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)
			break;
		int a[100000],maxn=-100000;	//最大值maxn初始化为0 
		int total=0;	//计算子序列和并赋初值0 
		int l,r,ll;	//l表示最大子序列起始点,r表示终止点,ll暂存每段子序列起始点 
		int num=0;	//计算负数的数量并赋初值 
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			if(a[i]<0)	//计算负数数量 
				num++;
			if(total<=0)	//如果子序列和小于等于0,就从下一个数重新累加 
			{
				total=a[i];
				ll=a[i];	//暂时存起始点 
			}
			else	//如果子序列和大于0,则继续累加 
				total+=a[i];
			if(total>maxn)	//如果该段子序列和大于最大值 
			{
				maxn=total;		//重新记录最大值 
				l=ll;		//记录起始点 
				r=a[i];	//记录终止点 
			}
		}
		if(num==n)	//如果全为负数,则子序列和为0,直接输出起始点和终止点 
			cout<<0<<" "<<a[1]<<" "<<a[n]<<endl;
		else	//否则输出最大子序列和,起始点和终止点 
			cout<<maxn<<" "<<l<<" "<<r<<endl;
	}
	return 0;
}


E - 迷宫(广搜)


原题链接: POJ 3984 迷宫问题

思路:

  1. dfs适合求可行性路径数目,而bfs适合求最短路径之类的(因为一旦搜到,就是最短)
  2. vis标记该点是否走过,map标记是否有陷阱,结构体q就是走过的点,每个点需要保存自己的坐标和上一个节点的,最终将记录的点输出。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; 
int map[6][6];	//地图 
int vis[6][6];	//标记是否走过 
int dx[4]={0,0,-1,1};	//横坐标方向 
int dy[4]={1,-1,0,0};	//纵坐标方向 
struct node
{
	int x;		//横坐标 
	int y;		//纵坐标 
	int pre;	//前一个点 
}q[6*6];
void print(int head)
{
	while(q[head].pre!=-1)	//当不是起始点时 
	{
		print(q[head].pre);	//递归前一个点 
		printf("(%d, %d)\n",q[head].x,q[head].y);
		return;
	}
	printf("(0, 0)\n");	//输出起始点 
}
void bfs(int x,int y)
{
	int head=0,tail=1;
	q[0].x=0;	//起始点 
	q[0].y=0;	//终止点 
	q[0].pre=-1;	//起始点的前一个点 
	node now;
	while(head<tail)	//当队列不为空 
	{
		if(q[head].x==4&&q[head].y==4)	//如果是终点 
		{
			print(head);	//遍历输出 
			return;
		}
		for(int i=0;i<4;i++)	//遍历搜索方向 
		{
			now.x=q[head].x+dx[i];	//横坐标遍历 
			now.y=q[head].y+dy[i];	//纵坐标遍历 
			now.pre=head;	//变为前一个点 
			if(now.x>=0&&now.x<=4&&now.y>=0&&now.y<=4)	//如果是边界内 
			{
				if(!map[now.x][now.y]&&!vis[now.x][now.y])	//如果没有陷阱且没走过 
				{
					vis[now.x][now.y]=1;	//标记走过 
					q[tail]=now;	 
					tail++;	//同节点遍历 
				}
			}
		}
		head++;	//节点+1,即下一节点 
	}
}
int main() 
{
	for(int i=0;i<=4;i++)
	{
		for(int j=0;j<=4;j++)
			scanf("%d",&map[i][j]);
	}
	memset(vis,0,sizeof(vis));	//全部标记为未走过 
	bfs(0,0);	//从起点开始搜索 
	return 0;
}


I - 数论小定理(费马小定理 构造)


原题链接: HDU 6440 Dream

思路:

费马小定理:若 p 是质数, a 与 p 互质,那么 a p − 1 ≡ 1 ( m o d ) p a^{p−1} ≡ 1 (mod) p ap11(mod)p构造: ( m + n ) p = ( m + n ) ( m o d ) p (m+n)^{p} = (m+n) (mod) p (m+n)p=(m+n)(mod)p所以 m p + n p = ( m + n ) ( m o d ) p m^{p} + n^{p}= (m+n) (mod) p mp+np=(m+n)(mod)p所以在模p下,公式恒成立: ( m + n ) p = m p + n p ( 0 ≤ m , n < p ) (m+n)^{^{p}}=m^{p}+n^{p}(0\leq m,n< p) (m+n)p=mp+np(0m,n<p)根据定义的加法运算与乘法运算可以直接运算得出。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std; 
int main() 
{
	int t;
	cin>>t;
	while(t--)
	{
		int p;
		cin>>p;
		for(int i=1;i<=p;i++)
		{
			for(int j=1;j<=p;j++)
			{
				printf("%d%c",((i-1)+(j-1))%p,j==p?'\n':' ');
			}
		}
		for(int i=1;i<=p;i++)
		{
			for(int j=1;j<=p;j++)
			{
				printf("%d%c",((i-1)*(j-1))%p,j==p?'\n':' ');
			}
		}
	}
	return 0;
}


J - 数论大定理(费马大定理 勾股数)


原题链接: HDU 6441 Find Integer

思路:

费马大定理:a^n + b^n = c^n 若n大于2则无正整数解。

  1. n=0的情况,肯定也不存在,不可能有1+1=1的情况。
  2. n=1的情况,b等于 a,c 等于 a*2 即符合表达式。
  3. n=2的情况,根据勾股数的性值可得出。

勾股数: a² + b² = c²

  1. 当a为大于1的奇数 2n+1 时,b = 2n² + 2n ,c = 2n² + 2n + 1
    故 n = (a-1)÷2 ,得 b = 2n² + 2n ,c = 2n² +2n +1
  2. 当a为大于4的偶数 2n 时,b = n² - 1 ,c = n² + 1
    故 n = a/2 ,得 b = a²/4 - 1 ,c = a²/4 + 1

代码如下:

#include <iostream>
#include <cstdio>
using namespace std; 
int main() 
{	
	//费马大定理!!! 
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,a;
		scanf("%d%d",&n,&a);
		if(n==0||n>2)
			printf("-1 -1\n");
		else if(n==1)
			printf("%d %d\n",a,a*2);
		else if(n==2)
		{
			if(a%2!=0)
				printf("%d %d\n",(a*a-1)/2,(a*a+1)/2);	//公式 
			else
			{
				if(a==2)	//a==2的情况不存在 
					printf("-1 -1\n");
				else 
					printf("%d %d\n",a*a/4-1,a*a/4+1);
			}
		}
	}
	return 0;
}


L - 钱币兑换(完全背包模板题)


原题链接: HDU 1284 钱币兑换问题

思路:注意数组要开大点!

  1. dp[i][j]表示前i种面值硬币狗造成j美分的方法数。
  2. dp[4][32800]全部初始化为0,dp[0][0]初始化为1。
  3. 状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-val[i]]
    val[i]表示第i种硬币的面值,方程前者表示不选第i种硬币的方法数,后者表示选到第i种硬币构造到 j - val[i] 美元的方法数。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[4][32800];	//数组要开大点 
int main()
{
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int i=1;i<=3;i++)
	{
		for(int j=0;j<32785;j++)	//j要从0开始 
			dp[i][j]=dp[i-1][j]+dp[i][j-i];
	}
	int n;
	while(scanf("%d",&n)!=EOF)
		printf("%lld\n",dp[3][n]);
	return 0;
}


M - 又是A+B(找规律)


原题链接: HDU 2524 矩形A + B

思路:数学题,排列组合

  1. 如果有1行m列,则总矩形数为 m+(m-1)+(m-2)+…+2+1 = (1+m) × m ÷ 2
  2. 如果有n行1列,则总矩形数为 n+(n-1)+(n-2)+…+2+1 = (1+n) × n ÷ 2
  3. 则如果有n行m列,总矩形数为 (1+n) × n ÷ 2 × (1+m) × m ÷ 2

代码如下:

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		printf("%d\n",(1+n)*n/2*(1+m)*m/2);
	}
	return 0;
}


N - 素数(素数筛)


原题链接: HDU 1262 寻找素数对

思路:

  1. 这道题,对于m,既然是要寻找最接近的素数对,那么只要从m/2往下找,只要一找到素数对就可以输出了,因为一定有一个素数小于等于m/2。
  2. 然后判断素数的,这里的代码是每输入一个数就遍历判断一次,当然也可以直接遍历一个很大的数,打表。

代码如下:

#include <iostream>
#include <cstdio>
using namespace std; 
int isPrime(int x)
{
	for(int i=2;i*i<=x;i++)	//素数返回1,合数返回0 
	{
		if(x%i==0)
			return 0;
	}
	return 1;
}
int main()
{
	int m;
	while(scanf("%d",&m)!=EOF)
	{
		for(int i=m/2;i>1;i--)
		{
			if(isPrime(i)&&isPrime(m-i))	//是素数对 
			{
				printf("%d %d\n",i,m-i);
				break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值