N诺刷题C++

Posted 猫头丁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了N诺刷题C++相关的知识,希望对你有一定的参考价值。

有向树形态

求N个结点能够组成的二叉树的个数
定义f(0)= 1
当只有一个节点时,只会生成一种类型的二叉树,记做f(1)= 1
当有两个节点时,首先固定一个节点(即固定根节点),剩下一个节点可以排放的位置为1= 1+0 = 0+1[即左边一个节点右边0个节点或者左边0个节点右边1个节点]记做f(2)= f(1)f(0) + f0f(1) = 2
当有三个节点,首先固定根基点,剩下3-1=2个节点的安放位置:2= 2
0 + 1
1 + 0*2 记做f(3)= f(2)*f(0) + f(1)*f(1) + f(0)*f(2) = 2 + 1 + 2 = 5

当有n个节点时,也是首先固定下根节点,剩下n-1个节点,可以如下排放:n-1= (n-1)*0 + (n-2)*1 + (n-3)2 + … + 0(n-1)记做f(n)= f(n-1)*f(0) + f(n-2)*f(1) + f(n-3)*f(2) + f(n-4)*f(3) + …f(0)*f(n-1)

#include<iostream>
using namespace std;
int main()
	int n;
	cin >> n;
	if(n < 2)
        cout << n;
	
	else
        long long dp[n+1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2 ; i <= n ; i++)
            long long res = 0;
            for(int j = 0 ; j < i ; j++)
                res = res + dp[j] * dp[i-1-j];
            
            dp[i] = res;
        
        cout << dp[n];
	
	return 0;

解一元一次方程

解方程,给定一个字符串,代表一个一元一次方程。如果有解求解,输出格式“x=数字“,如果解的个数无穷,输出 “infinite solutions”。如果没有解输出“no solution”,字符串长度不超过 256 。
这道题,我也没想出来什么好办法,就用if写吧

#include<iostream>
#include<cstdio>
using namespace std;
int main()
	string str;
	cin >> str;
	int flag = 1;
	int a = 0, b = 0;
	int i = 0;
	while(str[i] != '=')
        if(str[i] == 'x')
            b = b + flag;
        
	    int temp = 0;
        while(str[i]>='0' && str[i]<='9')
            temp = temp * 10 + str[i] - '0';
            i++;
        
        if(str[i] != 'x')
            a = a + flag * temp;
        
        else
            b = b + flag * temp;
            i++;
        
        if(str[i] == '-')
            flag = -1;
        
        else
            flag = 1;
        
        if(str[i] == '=')
            break;
        
        i++;
	
	flag = -1;
	while(i < str.size())
        if(str[i] == 'x')
            b = b + flag;
        
	    int temp = 0;
        while(str[i]>='0' && str[i]<='9')
            temp = temp * 10 + str[i] - '0';
            i++;
        
        if(i == str.size() || str[i] != 'x')
            a = a + flag * temp;
        
        else
            b = b + flag * temp;
            i++;
        
        if(str[i] == '-')
            flag = 1;
        
        else
            flag = -1;
        
        i++;
	
	if(a==0 && b==0)
        cout << "infinite solutions";
	
	else if(b==0)
        cout << "no solution";
	
	else
        cout << "x=" << (-a)/b;
	
	return 0;

整数拆分

一个整数总可以拆分为2的幂的和,例如: 7=1+2+4 7=1+2+2+2 7=1+1+1+4 7=1+1+1+2+2 7=1+1+1+1+1+2 7=1+1+1+1+1+1+1 总共有六种不同的拆分方式。 再比如:4可以拆分成:4 = 4,4 = 1 + 1 + 1 + 1,4 = 2 + 2,4=1+1+2。 用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。

对于一个数字 n,如果 n 是奇数,那么 n 的所有组合方式中一定包含一个 1,那么它和 n-1 的组合方式种数相同,dp[n] = dp[n-1];

如果 n 是偶数,那么它的组合方式中可能有 1,也可能没有 1,有 1 的组合方式有 dp[n - 1] 种,没有 1 的组合方式有 dp[n/2] 种,因为偶数组合方式除以 2 后的组合方式其实是一样的,dp[n] = dp[n-1] + dp[n/2]

#include<iostream>
using namespace std;
int main()
	int n;
	while(cin >> n)
        if(n < 2)
            cout << n << endl;
        
        else
            long dp[n+1];
            dp[1] = 1; 
            for(int i = 2 ; i <= n ; i++)
                if(i%2 == 0)
                dp[i] = (dp[i-1] + dp[i/2])%1000000000;
                  
               else
                dp[i] = dp[i-1];
                
            
            cout << dp[n]<< endl;
        
	
	return 0;

最小邮票数

有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。 如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
	int n, k;
	cin >> n >> k;
	int num[k];
	for(int i = 0 ; i < k ; i++)
        cin >> num[i];
	
	int dp[n+1];
	for(int i = 0 ; i <= n ; i++)
        dp[i] = 1000;
	
	dp[0] = 0;
	for(int i = 0 ; i < k ; i++)
        for(int j = n ; j >= num[i] ; j--)
            if(dp[j-num[i]] != 1000)
                dp[j] = min(dp[j], dp[j-num[i]]+1);
        
        for(int j = 0 ; j <= n ; j++)
            cout << dp[j] << ' ';
        
        cout << endl;
	
	if(dp[n] == 1000)
        cout << 0;
	
	else
        cout << dp[n];
	
	return 0;

过河卒

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点 (0, 0)、B 点 (n, m),同样马的位置坐标是需要给出的。

思想还是之前的动态规划,只要避开马和马会经过的点就好了。

#include<iostream>
using namespace std;
int main()
	int bx, by, mx, my;
	while(cin >> bx >> by >> mx >> my)
        long long bp[by+1][bx+1];
        for(int i = 0 ; i <= by ; i++)
            for(int j = 0 ; j <= bx ; j++)
                if(i==0 || j==0)
                    bp[i][j] = 1;
                else
                    bp[i][j] = 0;
            
        
        for(int i = 1 ; i <= by ; i++)
            for(int j = 1 ; j <= bx ; j++)
                if((abs(i-my)==1 && abs(j-mx)==2) || (abs(i-my)==2 && abs(j-mx)==1)||(i==my && j==mx))
                    bp[i][j] = 0;
                
                else
                    bp[i][j] = bp[i-1][j] + bp[i][j-1];
                
            

        
        cout << bp[by][bx] << endl;
	
	return 0;

路径计数2

一个N×N的网格,你一开始在(1,1),即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N,N),即右下角有多少种方法。
但是这个问题太简单了,所以现在有M个格子上有障碍,即不能走到这M个格子上。

注意当这个障碍点在边界上,即上边界或是左边界,在它之后的位置点路径方法都为0

#include<iostream>
using namespace std;
int main()
	int n, m;
	while(cin >> n >> m)
        long long bp[n+1][n+1];
        long long matrix[n+1][n+1];
        for(int i = 0 ; i <= n ; i++)
            for(int j = 0 ; j <= n ; j++)
                matrix[i][j] = 1;
                bp[i][j] = 0;
            
        
        int x, y;
        for(int i = 0 ; i < m ; i++)
            cin >> x >> y;
            matrix[y][x] = 0;
        
        for(int i = 0 ; i <= n ; i++)
            **if(matrix[1][i] != 0)
                bp[1][i] = 1;
            else
                break;
        
        for(int i = 0 ; i <= n ; i++)
            if(matrix[i][1] != 0)
                bp[i][1] = 1;
            else
                break;
        
        for(int i = 2 ; i <= n ; i++)
            for(int j = 2 ; j <= n ; j++)**
                if(matrix[i][j] == 0)
                    bp[i][j] = 0;
                
                else
                    bp[i][j] = bp[i-1][j] + bp[i][j-1];
                
            

        
        cout << bp[n][n] << endl;
	
	return 0;

Windy数

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?

数位dp,现在还有点不太理解

#include<iostream>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
#define MAX 1000005
#define vec vector<ll>
#define P pair<int,int>

int dp[15][10];//dp[i][j]:位数为i,最高位为j时最多有多少windy数

//统计[1,x)区间内的windy数
int cal(int x) 
	int a[11], len = 0;
	while (x > 0) 
		a[++len] = x % 10;
		x /= 10;
	
	int sum = 0;
	//1:统计位数比x少的
	for (int i = 1; i < len; i++)
		for (int j = 1; j < 10; j++)//注意这里从1开始,最高位不能为0!
			sum += dp[i][j];
	//2:统计位数和x一样,但是首位比x小的,同样最高位不能为0
	for (int i = 1; i < a[len]; i++)
		sum += dp[len][i];
	//3:统计位数与x一样,首位也一样的,那么之前的每一位加上限制a[i]
	for (int i = len - 1; i >= 1; i--) 
		for (int j = 0; j < a[i]; j++)  //注意该位的限制,由于不是最高位,可以从0开始
			if (abs(j - a[i + 1]) >= 2)//该位和上一位满足要求
				sum += dp[i][j];
		if (abs(a[i + 1] - a[i]) < 2)//传入数字连续两位不满足了
			break;
	
	return sum;


int main() 
	memset(dp, 0, sizeof(dp));
	for (int i = 0; i < 10; i++)dp[1][i] = 1;//预处理第一位

	for (int i = 2; i < 11; i++) //遍历每2-10每个位数,1预处理了
		for (int j = 0; j < 10; j++) //遍历这一位的每种可能数字
			for (int k = 0; k < 10; k++) //遍历上一位的每种可能数字
				if (abs(k - j) >= 2) //相邻数字之差大于2
					dp[i][j] += dp[i - 1][k];
	int a, b;
	while (cin >> a >> b) 
		if (a > b)swap(a, b);
		cout << cal(b + 1) - cal(a) << endl;
	

以上是关于N诺刷题C++的主要内容,如果未能解决你的问题,请参考以下文章

2020-3-9刷题

bzoj1426 收集邮票

bzoj1426 收集邮票

Bzoj 1426 收集邮票

BZOJ1426收集邮票 期望

BZOJ1426: 收集邮票 期望DP