ACM热身赛2——主要包括递推专题和简单数学知识

28 篇文章 0 订阅
13 篇文章 0 订阅

ACM热身赛——递推,数学专题

A(统计次数)

在这里插入图片描述

水题,但我却提交了8次错误,没搞明白题目意思,大写字母也算,自己写的时候只考虑了小写字母

#include <iostream>
#include <cstdlib>
#include<cstring>
int main()
{
    int n;
    char s[105];
    scanf("%d",&n);
    getchar();
    while(n--)
    {
        gets(s);
        int a=0,e=0,p=0,o=0,u=0;
        for(int i=0;i<strlen(s);i++)
        {
            if(s[i]=='a'||s[i]=='A')
                a++;
            else if(s[i]=='e'||s[i]=='E')
                e++;
            else if(s[i]=='i'||s[i]=='I')
                p++;
            else if(s[i]=='o'||s[i]=='O')
                o++;
            else if(s[i]=='u'||s[i]=='U')
                u++;
        }
        printf("a:%d\n",a);
        printf("e:%d\n",e);
        printf("i:%d\n",p);
        printf("o:%d\n",o);
        printf("u:%d\n",u);
        if(n!=0) printf("\n");
    }
    return 0;
}

B(最小公倍数和最大公因数的求解)

在这里插入图片描述

这题应该可以直接暴力求解,超不超时就不知道了。

非暴力求解涉及到的数学知识:

  • 三个数的最小公倍数是其中两个数的最小公倍数和剩下一个数的最小公倍数,这个很容易,不推导

  • 设x,y的最大公因数fun(x,y),那么x,y的最小公倍数为x*y/fun(x,y)

    证明:因为如果找到了两个数a,b的最大公约数c,那么假设a=mc,b=nc,那么可以肯定,n,m没有公约数,即m,n相互之间的关系相当于两个质数之间的关系。那么求a,b的最小公倍数d,就相当于求m,n的最小公倍数乘以c,所以ab=cd

  • 辗转相除法求最大公因数

    算法过程:假设a>b,先用a%b=c(b>=c),把b赋值给a,c赋值给b,那么下一次
    就是b%c=d(c>=d),把c赋值给b,d赋值给c。那么下一次就是:c%d=e…
    一直到n%m==0,那么m就是最大公约数。

    证明:假设求最大公因数的函数为gcd(a,b)且a>b,证明该方法就是证明gcd(a,b)gcd(b,a%b),直到a%b0,最后取b。其实很简单,因为a-a%b可以整除b,a%b和b都可以整除gcd(b,a%b),a不可以整除以b,所以gcd(a,b)==gcd(b,a%b)。

    现在思路就很清晰了,两步:

    1. 求最大公因数

    2. 用x*y求最小公倍数,注意int会溢出

       #include<iostream>
       using namespace std;
       long long gcd(long long a, long long b)
       {
       	long long d = 1;
       	long long c=0;
       	while (d)
       	{
       		c = a / b;
       		d = a % b;
       		a = b;
       		b = d;
       	}
       	return a;
       }
       int main()
       {
       	long long n;
       	while (cin >> n)
       	{
       		long long* nums = new long long[n];
       		for (int i = 0; i < n; i++)
       			cin >> nums[i];
       		if (n == 1)
       			cout << nums[0] << endl;
       		else
       		{
       			for (int i = 0; i + 1 < n; i++)
       			{
       				nums[i + 1] = nums[i] * nums[i + 1] / gcd(nums[i], nums[i + 1]);
       			}
       			cout << nums[n - 1] << endl;
       		}
       	}
       	return 0;
       }
       
    

C(简单双指针)

在这里插入图片描述

挺水的一题,但是它是我提交错误第二多的一个题…没用string,直接字符数组,问就是之前拿string没过,就改了写法

#include<iostream>
using namespace std;
int main(){
    int n;
    cin>>n;
    char str[1000];//还可以写成string s;
    for(int k=0;k<n;k++)//一定要一个大循环,因为要循环几组数据进行一下操作
    {
        bool ans=true;//写在循环里,每次重新变为true,因为要对每一组进行
        cin>>str;
        for(int i=0,j=strlen(str)-1;i<j;i++,j--)//strlen()可写成s.size()
        {
            if(str[i]!=str[j])
            {
                ans=false;
                break;//一个一个判断所以要跳出来
            }
        }
        if(ans==true) cout<<"yes"<<endl;
        else cout<<"no"<<endl;
    }
    return 0;
}

D(汉字编码问题)

在这里插入图片描述

注意下汉字的编码,**汉字编码是负的,且占两个字节,也就是说如果连续两个字节都是负的,那他们就是汉字,且要注意,键盘输入的字符除了汉字外不会出现负的,所以我们这个题只要统计负数编码数量后除以2就可以得到答案。**这题也提交错了一次,想多了,以为题目说的汉字必须是不包括中文符号的,但是要真这么出我们也要会写。如果是不包括中文符号,**只要我们把“啊”的编码输出出来就明白了,两个字节和负数的基础上加一条第一个字节大于等于-80,第二个字节大于等于-95。**下面是这个题的代码

#include<iostream>
using namespace std;
char a[100005];
int main()
{
	int n;
	while(cin >> n)
	{
		int temp = n - 1;
		while(n--)
		{
			char c;
			int count = 0, i = 0;
			if(n == temp)
				getchar();
			while(c = getchar())
			{
				if(c == '\n') break;
				if(c < 0) count++;
				a[i++] = c;
			}
			cout << count / 2 <<endl;
		}
	}
}

E(进制转换)

在这里插入图片描述

稍微用一下string的特性,把数字一个个把余数放进去,再反转,注意负号的处理

#include<iostream>
#include<string>
using namespace std;
char fun(long long x)
{
	if (x <= 9)
		return '0' + x;
	else
		return x - 10 + 'A';
}
int main()
{
	long long n, m;
	while (cin>>n>>m)
	{
		string s;
		if (n < 0)
		{
			s += '-';
			n = -n;
		}
		while (1)
		{
			s += fun(n % m);
			n = n / m;
			if (n == 0)
				break;
		}
		if (s[0] != '-')
			reverse(s.begin(), s.end());
		else
			reverse(s.begin() + 1, s.end());
		cout << s << endl;
	}
	return 0;
}

F(递推)

在这里插入图片描述

直角三角形输出杨辉三角还是很简单的,先列表再输出,注意一下每组输出后有一个空行

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int n;
	int nums[30][30] = { 0 };
	for (int i = 0; i < 30; i++)
		nums[i][0] = nums[i][i] = 1;
	for(int i=0;i<30;i++)
		for (int j = 1; j < i; j++)
		{
			nums[i][j] = nums[i - 1][j - 1] + nums[i - 1][j];
		}
	while (cin >> n)
	{
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j <= i; j++)
			{
				cout << nums[i][j];
				if (j == i)
					cout << endl;
				else
					cout << " ";
			}
		}
		cout << endl;
	}
	return 0;
}

G(进制)

在这里插入图片描述

简单的进制问题

#include<iostream>
#include<string>
using namespace std;
int main()
{
	long long ah, am, as, bh, bm, bs, n;
	cin >> n;
	while (n--)
	{
		cin >> ah >> am >> as >> bh >> bm >> bs;
		as += bs;
		am += bm;
		ah += bh;
		if (as >= 60)
		{
			am += as / 60;
			as %= 60;
		}
		if (am >= 60)
		{
			ah += am / 60;
			am %= 60;
		}
		cout << ah << " " << am << " " << as << endl;
	}
	return 0;
}

H(查找)

在这里插入图片描述

要是这题需要以原来的顺序输出那还麻烦点,不过他要求顺序输出那就不客气了,先排序,后双指针分别指向两个数组,谁小移谁,代码有一点臃肿,过了就没想写简洁一点了

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	int n, m,x;
	while (cin >> n >> m)
	{
		if (m == 0 && n == 0)
			break;
		vector<long long>a, b;
		for (int i = 0; i < n; i++)
		{
			cin >> x;
			a.push_back(x);
		}
		for (int i = 0; i < m; i++)
		{
			cin >> x;
			b.push_back(x);
		}
		sort(a.begin(), a.end());
		sort(b.begin(), b.end());
		long l=0,r=0,num=0;
		while (l < n && r < m)
		{
			if (a[l] == b[r])
			{
				a[l] = INT_MIN;
				l++;
				r++;
				num++;
			}
			else if (a[l] > b[r])
			{
				r++;
			}
			else
			{
				l++;
			}
		}
		if (num == n)
			cout << "NULL";
		else
			for (int i = 0; i < n; i++)
			{
				if (a[i] != INT_MIN)
				{
					cout << a[i] << " ";
				}
			}
		cout << endl;
	}
	return 0;
}

I(余数的性质)

在这里插入图片描述

数学知识,取余相乘后再取余与原数取余的结果一样

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	long long n, m;
	while (cin >> n >> m)
	{
		if (n == 0 && m == 0)
			break;
		int x = n;
		for (int i = 1; i < m; i++)
		{
			n = (n % 1000) * x;
		}
		cout << n % 1000 << endl;
	}
	return 0;
}

J(正弦定理,叉乘的性质,多边形分割)

在这里插入图片描述

首先我们可以想到把多边形分为多个三角形,怎么分?

我们固定其中一个点,按照题目给出的输入方向逆时针依次取其他的相邻两个点,这样就转换成了求这些三角形的面积和。

于是这题可以考虑两种解法,一个使用欧拉公式,不过讨论起来有点麻烦且时间复杂度高,我们换一种思维;

我们高中学过正弦定理1/2|a||b|sin<a,b>,假设a,b都是从固定点到其他点的两个向量。那么,接下来怎么走,我们考虑题目给的是坐标,坐标的话,我们要是直接求长度和角度会显得复杂且精度会丢失,我们可以把|a||b|sin<a,b>当成整体,而这个整体,就是a向量和b向量叉乘的模,这是高等数学下最前面的只是,而它叉乘出来的向量,如果我们沿着逆时针方向,出来的向量必然是沿着空间坐标系中的Z轴正方向,这个时候,它的模,就等于它的向量代数式上的Z的大小,因为X,Y上都为0,想不出拿笔算一下就知道了。

#include <iostream>
using namespace std;
struct triangle
{
	int x;
	int y;
}a[100];
double f(int x, int y, int x1, int y1, int x2, int y2)
{
	return (x * y1 + x1 * y2 + x2 * y - x * y2 - y * x1 - x2 * y1) / 2.0;//展开后的形式
}
int main()
{
	int n;
	while (cin >> n)
	{
		if (n == 0) break;
		for (int i = 0;i < n;i++)
		{
			cin >> a[i].x >> a[i].y;
		}
		double area = 0;
		for (int i = 1;i < n - 1;i++)
		{
			area += f(a[0].x, a[0].y, a[i].x, a[i].y, a[i + 1].x, a[i + 1].y);
		}
		printf("%.1lf\n", area);
	}
	return 0;
}

K(贪心,排序)

在这里插入图片描述

这个题考的是贪心,**我们只要把每件事情按截止时间排个序,再每次选上个事件结束后截止最早的事情就行了。可行性简单分析一下,举个例子,我有一个在3点干完的事情和一个4点干完的事情,我选择三点干完的,那么我就有可能选到3.30开始干活的,要是没有3点到四点之间开始的活,那也没亏,休息一个小时还不好吗?**但要是题目让我们求出最多可以干多少时间就是另一回事了,所幸我们是求最多能干几个事情

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct Data {
	int x=0;
	int y=0;
};
bool cmp(Data a, Data b)
{
	return a.y< b.y;
}
int main()
{
	int n;
	while (cin >> n)
	{
		if (n == 0)
			break;
		vector<Data>data(n);
		for (int i = 0; i < n; i++)
		{
			cin >> data[i].x >> data[i].y;
		}
		sort(data.begin(), data.end(), cmp);
		int num = 0, i = 0,t=0;
		while (i < n)
		{
			if (data[i].x >= t)
			{
				num++;
				t = data[i].y;
			}
			i++;
		}
		cout << num << endl;
	}
	return 0;
}

L(三角形组成条件)

在这里插入图片描述

非常水的一个题了

#include<iostream>
using namespace std;
int main()
{
	double a, b, c;
	int n;
	cin >> n;
	while (n--)
	{
		cin >> a >> b >> c;
		if (a + b > c && a + c > b && b + c > a)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
	}
	return 0;
}

M(真因数之和)

在这里插入图片描述

思路也不难,这个数据量不大,如果数据大点要注意一下数据溢出问题,还有就是1和根号n要单独考虑下

#include<iostream>
using namespace std;
int fun(int n)
{
	int sum = 0;
	int x = sqrt(n);
	for (int i = 2; i <=x; i++)
	{
		if (n % i == 0)
			if (i != x)
				sum = sum + i + n / i;
			else
				sum = sum + i;
	}
	return sum+1;
}
int main()
{
	int a, b;
	int m;
	cin >> m;
	while (m--)
	{
		cin >> a >> b;
		if (fun(a) == b || fun(b) == a)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
	}
	return 0;
}

N(直接斐波那契)

在这里插入图片描述

这种简单的递推式就没必要递归写了,先列表

#include<iostream>
using namespace std;
int main()
{
	int m,n;
	long long answer[41];
	for (int i = 1; i <= 40; i++)
	{
		if (i == 1 || i == 2)
			answer[i] = i;
		else
			answer[i] = answer[i - 1] + answer[i -2];
	}
	cin >> n;
	while (n--)
	{
		cin >> m;
		cout << answer[m-1] << endl;
	}
	return 0;
}

O(简单递推)

在这里插入图片描述

这个主要是觉得这个收费站有病,递推很简单,2n-2一直推上去

#include<iostream>
using namespace std;
int main()
{
	int n, a;
	cin >> n;
	while (n--)
	{
		cin >>a;
		int num = 3;
		for (int i = 1; i <= a; i++)
			num = 2 * num - 2;
		cout << num << endl;
	}
	return 0;
}

P(统计次数)

在这里插入图片描述

也挺简单一个题,遍历存储到一个标志数组就行就行

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string key;
	int m;
	cin >> m;
	while (m--)
	{
		cin >> key;
		int flag[4] = { 0 };
		int len = key.length();
		if (len >= 8 && len <= 16)
		{
			for (int i = 0; i < len; i++)
			{
				if (key[i] >= 'A' && key[i] <= 'Z')
					flag[0]++;
				else if (key[i] >= 'a' && key[i] <= 'z')
					flag[1]++;
				else if (key[i] >= '0' && key[i] <= '9')
					flag[2]++;
				else
					flag[3]++;
			}
		}
		int num = 0;
		for (int i = 0; i < 4; i++)
			if (flag[i])
				num++;
		if (num >= 3)
			cout << "YES" << endl;
		else
			cout << "NO"<<endl;
	}
	return 0;
}

Q(斐波那契变式)

在这里插入图片描述

这题仔细分析一下其实也是斐波那契数列的变式

#include<iostream>
using namespace std;
int main()
{
	int m, n;
	long long answer[50];
	for (int i = 1; i <= 49; i++)
	{
		if (i == 1 || i == 2)
			answer[i] = i;
		else
			answer[i] = answer[i - 1] + answer[i - 2];
	}
	cin >> n;
	int a, b;
	while (n--)
	{
		cin >> a>>b;
		cout << answer[b-a] << endl;
	}
	return 0;
}

R(递推分类讨论,固定n-1项)

在这里插入图片描述

这题开始有意思了,首先用数字代替颜色相信大家没问题,第一思路是直接回溯套循环求解,代码如下

#include<iostream>
#include<vector>
using namespace std;
void fun(vector<int> nums, int form, int to,long long &num )//从第二个元素开始
{
	if (form == to&&to!=0)
	{
		for (int i = 1; i <= 3; i++)
			if (i != nums[0] && nums[to - 1] != i)
				num++;
		return;
	}
	else
	{
		for (int i = 1; i <= 3; i++)
		{
			if (i != nums[form - 1])
			{
				nums[form] = i;
				fun(nums, form + 1, to, num);
			}
		}
	}
}
int main()
{
	int n;
	while (cin >> n)
	{
		long long num = 0;
		vector<int>nums(n);
		if (n == 1)
			num = 3;
		else
			for (int i = 1; i <= 3; i++)
			{
				nums[0] = i;
				fun(nums, 1, n - 1, num);
			}
		cout << num << endl;
	}
	return 0;
}

程序的测试用例我全部试了一遍,对是对了,就是会超时,你想想3的50次方多大,于是,逼迫我走上了递推的道路。

我们首先我们很容易得到,nums[1]=3,nums[2]=6,nums[3]=6,接着,我们假设要开始上第n个格子(n>3),那么

有如下两种情况:

  1. 第n-1个格子的颜色!=第一个格子颜色,此时第n个格子只有一个选择,又因为求nums[n-1]刚好表示了第n-1个的颜色不等于第一个的颜色,得这中情况的选择有nums[n-1]种;
  2. 第n-1个格子的颜色==第一个格子颜色,这时第n个格子颜色不等于第一个格子颜色和第n-1个格子颜色,也就是第n-1只随着第一个格子变化,也就是说取决于nums[n-2],因为第n个格子也有两种选择,最终的方案数目就是2*nums[n-2];

合在一起,得到递推式nums[n]=nums[n-1]+nums[n-2](n>=4)

#include<iostream>
using namespace std;
int main()
{
	int n;
	long long answer[51];
	answer[1] = 3;
	answer[2] = answer[3] = 6;
	for (int i = 4; i <= 50; i++)
		answer[i] = answer[i - 1] + answer[i - 2] * 2;
	while (cin >> n)
	{
		cout << answer[n] << endl;
	}
}

S(斐波那契变式)

在这里插入图片描述

这题自己画一下或者直接看题目给的图就可以明白这又是一个斐波那契数列

#include<iostream>
using namespace std;
int main()
{
	long long answer[51];
	int n;
	for (int i = 1; i <= 50; i++)
		if (i == 1 || i == 2)
			answer[i] = i;
		else
			answer[i] = answer[i - 1] + answer[i - 2];
	while (cin >> n)
	{
		cout << answer[n] << endl;
	}
	return 0;
}

T(递推分类讨论固定第n项)

在这里插入图片描述

这题我刚开始进了一个误区,开始推出来的递推式为f3=3f2-f1,我的思路是,先把所有的情况求出来,也就是3f2,再把倒数第一个和倒数第二个都等于O的情况减掉,也就是减去f1,不知道各位知道错哪里了吗?这里面的误区就是,当我选择让倒一和倒二同时为O的时候,没有排除第一位不能为O的情况,也就是说最后减多了,导致结果偏小。

好了,回归题目正解。

和上面一个题差不多,分两种情况,不过这次我还是有一点不一样,我们从最后一个推前面,也是两种情况:

  1. 若最后一个取O,那倒数第二个一定不能取O,倒数第二个不取O,有两种取法,同时取法除去最后两个元素的n-2个元素不受到后面的影响,也就是这种情况下得到的取法有2*f1种;
  2. 若最后一个不取O,也有两种取法,和上面第一种情况差不多,n-1个元素不受到后面的影响,得到的取法有2*f2种

最后得到递推式f3=2f2+2f1,当然,也要从第三个数开始

#include<iostream>
using namespace std;
int main()
{
	int n;
	long long answer[41];
	answer[1] = 3;
	answer[2] = 8;
	for (int i = 3; i <= 40; i++)
	{
		answer[i] = answer[i - 1] * 2 +answer[i - 2]*2;//反着推,固定要加入的f3,若为O,那么f2只有两个选择,得2*f1,若不为,f3有两个取法,得2*f2
	}
	while (cin >> n)
	{
		cout << answer[n]<<endl;
	}
	return 0;
}

U(递推分类讨论——错排,随意固定中间一项)

在这里插入图片描述

错排题是第一次接触,刚开始想直接推表达式,算了两张草稿纸没推出来,主要错误是没有从中任意选一本进行考虑,而是和前面几个题一样从最后考虑,最后才考虑了随机选择。

首先,什么是错排问题:

一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题

下面详细分析一下:

第一步:

​ 我们取一本书,书的编号为m,现在这本书就在我们手中,注意,按照题目要求,最开始的时候这本书的位置号也是m号,按照题目要求,我们现在放书时不能放回这个位置m了,而是要选择其他位置,那么有多少种选择呢,想一下,总共有n本书,n个位置,现在我手里这本书不能把它放到位置m,那么剩下的n-1个位置我当然就是随便扔啦,也就是n-1种扔法,好,现在,我选择了位置k,我决定把手里这本书放到位置k这里,记住这个是位置编号k,那么,我肯定要把原来这里的编号为k的书拿出来,再把这本编号为n的书放进去喽。所以,现在我们手里的书的编号是k

第二步:

​ 我们把手里这本编号为k的书本放到书架,注意,放的过程中我们又面临两种情况,可以想到,此时此刻现在书架上编号m的位置是空着的,所以我们可以选择放在这个位置上,书的编号为k,位置编号为m,没错,满足题意,这是第一种情况,还有一种就是我不选择这个空着的位置m,我再重新选择一个新的位置,我们称之为第二种情况,下面详细分析

**第一种情况:**我把这本编号为k的书放到这个编号为m的地址,那现在我们面前是什么状况呢,就是位置k和位置m的书交换位置,也就是位置号不等于书号,即满足错排,总共n个位置,我们只动了m和k这两个位置,那么剩下的n-2个位置还是纹丝不动,保持一一对应的关系呢,那么对于剩下的这n-2本书的错排操作,我们又回到了问题的起点,求n-2本的错排操作数D(n-2),结合第一步,我们可以得到第一种情况总共有(n-1)*D(n-2)种方法

**第二种情况:**我们不选择这个空着的位置m啦,我们手持这本编号为k的书,我们从除了位置m以及位置k的剩下的n-2个位置中选择一个位置,OK,我们现在开始想,我手里这本书不能放在这个位置m,嗯嗯,除了第一步我们放置的那本书m不用管了,我们还要搞手里这本和剩下的n-2本,也就是n-1本,同时又要求手里这本k还不能放到位置m,这是不是就相当于把手里这本加上剩下的n-2本也就是n-1本书进行错排呢,哇哇哇,想一想,错排的定义,要求每本书都不能呆在某一个特定位置,是不是刚好符合呢,我们只要把第m个位置当成特定位置就行了,所以,现在的为题就到了求手里这本和剩下的n-2本总共是n-1本书的错排操作数,我们记为D(n-1),结合第一步,我们得出这第二种情况共有(n-1)*D(n-1)种方法

好的,现在我们总结两种情况,得到的递推式:

D(n)=(n-1)*[D(n-1)+D(n-2)]

#include<iostream>
using namespace std;
double fun(double n)
{
   double x = 1;
   for (int i = 1; i <= n; i++)
   {
   	x *= i;
   }
   return x;
}
int main()
{
   int n,t;
   double answer[21];
   answer[1] = 0;
   answer[2] = 1;
   for (int i = 3; i <= 20; i++)
   {
   	answer[i] = (i - 1) * (answer[i - 1] + answer[i - 2]);
   }
   cin >> t;
   while (t--)
   {
   	cin>>n;
   	printf("%lf%%\n", answer[n]/fun(n)*100);
   }
   return 0;
}

V(错排变式)

在这里插入图片描述

思路和上面一个题差不多,就是一个错排的变式,我们只要再多乘一个组合数就行了

但是这题我有个问题就是原来我是用的double,错了,我把关键字全部改成longlong就对了,不知道为什么?是因为浮点数乘除的精度问题吗?求一个大佬解释

#include<iostream>
using namespace std;
long long fun(long long n,long long m)
{
	long long x = 1;
	for (int i = n; i>n-m; i--)
	{
		x *= i;
	}
	for (int i = m; i >= 1; i--)
	{
		x /= i;
	}
	return x;
}
int main()
{
	long long n,m,t;
	long long answer[21];
	answer[1] = 0;
	answer[2] = 1;
	for (int i = 3; i <= 20; i++)
	{
		answer[i] = (i - 1) * (answer[i - 1] + answer[i - 2]);
	}
	cin >> t;
	while (t--)
	{
		cin>>n>>m;
		printf("%lld\n", fun(n,m)*answer[m]);
	}
	return 0;
}

W(折线分割递推)

在这里插入图片描述

这题其实也考了数学知识,所考察的数学知识直接看结论可能让人有点知其然不知所以然,我们分两步解释一下:

一、n条直线最多分平面问题

题目大致如:n条直线,最多可以把平面分为多少个区域。

可能你以前就见过这题目,这充其量是一道初中的思考题。当有n-1条直线时,平面最多被分成了f(n-1)个区域。则第n条直线要是切成的区域数最多,就必须与每条直线相交且不能有同一交点。 这样就会得到n-1个交点。这些交点将第n条直线分为2条射线和n-2条线段。而每条射线和线断将以有的区域一分为二。这样就多出了2+(n-2)个区域。

故:f(n)=f(n-1)+n

=f(n-2)+(n-1)+n

……

=f(1)+1+2+……+n

=n(n+1)/2+1

二、折线分平面(hdu2050)

根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当n-1条折线时,区域数为f(n-1)。为了使增加的区域最多,则折线的两边的线段要和n-1条折线的边,即2(n-1)条线段相交。那么新增的线段数为4(n-1),射线数为2。为什么线段数不是4(n-2),这里需要注意:折线本身相邻的两线段有一个交点,且所以只能增加一个区域。所以线段增加的区域数为4(n-1)-1,射线为2

故:f(n)=f(n-1)+4(n-1)+2-1

=f(n-1)+4(n-1)+1

=f(n-2)+4(n-2)+4(n-1)+2

……

=f(1)+4+4*2+……+4(n-1)+(n-1)

=2n^2-n+1

现在就很清楚了,上代码

#include<iostream>
using namespace std;
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int t;
        cin>>t;
        cout<<2*t*t-t+1<<endl;
    }
    return 0;
}

X(进制转换)

在这里插入图片描述

水题,大概意思就是要把十进制转换为2进制,且给的数都是正数

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int n;
	while (cin >> n)
	{
		string s;
		while (1)
		{
			s += to_string(n % 2);
			n /= 2;
			if (n == 0)
				break;
		}
		reverse(s.begin(), s.end());
		cout << s << endl;
	}
	return 0;
}

Y(格式输出)

在这里插入图片描述

注意格式输出,四个角上都是+,且不计入长度,注意每组输出后有一个空行

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int w,h;
	while (cin >> w >> h)
	{
		int i = 0, j = 0;
		for (i = 0; i < h + 2; i++)
		{
			for (int j = 0; j < w + 2; j++)
			{
				if ((j == 0 || j == w + 1) && (i == 0 || i == h + 1))
					cout << "+";
				else if (i == 0 || i == h + 1)
					cout << "-";
				else if ((j == 0 || j == w + 1))
					cout << "|";
				else
					cout << " ";
			}
			cout << endl;
		}

		cout << endl;
	}
	return 0;
}

Z(因数和奇偶判断)

在这里插入图片描述

这题的数据求解不难,就是这个output格式输出看了属实脑袋疼,没做出来,后来发现只要输出数据就行…英语不行的后果

思路就是求它每盏灯有多少个因子,然后判断奇偶

#include <iostream>  
using namespace std;  
int main()  
{  
    int n;  
    while (cin >> n)  
    {  
        int Lu = 0;  
        for (int i = 1; i <= n; i++)  
            if (n%i == 0)  
                Lu++;  
        if (Lu%2 == 0)  
            cout << "0\n";  
        else  
            cout << "1\n";  
    }  
    return 0;  
}  

760)]

注意格式输出,四个角上都是+,且不计入长度,注意每组输出后有一个空行

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int w,h;
	while (cin >> w >> h)
	{
		int i = 0, j = 0;
		for (i = 0; i < h + 2; i++)
		{
			for (int j = 0; j < w + 2; j++)
			{
				if ((j == 0 || j == w + 1) && (i == 0 || i == h + 1))
					cout << "+";
				else if (i == 0 || i == h + 1)
					cout << "-";
				else if ((j == 0 || j == w + 1))
					cout << "|";
				else
					cout << " ";
			}
			cout << endl;
		}

		cout << endl;
	}
	return 0;
}

Z(因数和奇偶判断)

[外链图片转存中…(img-R8mD5wWb-1657683026761)]

这题的数据求解不难,就是这个output格式输出看了属实脑袋疼,没做出来,后来发现只要输出数据就行…英语不行的后果

思路就是求它每盏灯有多少个因子,然后判断奇偶

#include <iostream>  
using namespace std;  
int main()  
{  
    int n;  
    while (cin >> n)  
    {  
        int Lu = 0;  
        for (int i = 1; i <= n; i++)  
            if (n%i == 0)  
                Lu++;  
        if (Lu%2 == 0)  
            cout << "0\n";  
        else  
            cout << "1\n";  
    }  
    return 0;  
}  

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值