7.位运算,递归,全排列

模板

递推/递归

斐波那契数列:
递推:

int a[1]=1,a[2]=1;
for(int i=3;i<=n;i++)
	a[i]=a[i-1]+a[i-2];

递归:

int fun(int n)
{
	if(n==1||n==2)	return 1;
	else	return fun(n-1)+fun(n-2);
}

一般不推荐使用递归的方法,递归在一次一次的调用中,会导致很大的空间和时间的浪费,很容易TLE。

全排列

next_permutation()/prev_permutation()
sort(a,a+n);
do
{
	for(int i=0;i<n;i++)
		cout<<a[i];
	cout<<endl;
}while(next_permutation(a,a+n);

练习

A - 前缀异或 51Nod - 2128

输入一个长度为n(1 <= n <= 100000)数组a1, a2, …, an。
输入一个询问数m(1 <= m <= 100000)和m组询问,每组询问形如(l, r)
对于每组询问(l, r),你需要输出al xor al+1 xor … xor ar−1 xor ar,即第l个数字到第r个数字的异或。
如果你的算法需要约n*m的时间,你将只能通过第一个测试点。
如果你的算法需要约n+m的时间,你将可以通过本题。
Input
第一行一个整数n 第二行为n个整数a1, a2, … an 第三行一个整数m 接下来m行,每行两个整数l, r表示询问。
Output
输出一共m行,对于每一个询问输出一个整数表示结果。
Sample Input
3
1 2 3
6
1 1
2 2
3 3
1 2
2 3
1 3
Sample Output
1
2
3
3
1
0
思路:
异或:“^”位运算,二者相同为0,不同为1。
两个公式:a^a=0;
0^a=a;
所以:

al^.....^ar=(a1^.....^al-1)^(a1^.....^al-1)^(al^.....^ar)
		   =(a1^.....^al-1)^(a1^.....^al-1^al^.....^ar)
		   =(a1^.....^al-1)^(a1^.....^ar)

AC代码:

#include <iostream>
using namespace std;
int a[100010];
int main()
{
	int n;
	cin>>n;
	a[0]=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		a[i]=a[i]^a[i-1];	//前缀和
	}	
	int m;
	cin>>m;
	while(m--)
	{
		int l,r;
		cin>>l>>r;
		int ans=a[l-1]^a[r];
		cout<<ans<<endl;
	}
	return 0;
}

C - Skill Up (DFS、枚举)AtCoder - abc167_c ⭐⭐

题目链接
题目大意:
小明想要学习M门算法,希望对于这M门算法,每一门的掌握程度都要大于X。
书店中共有N 本书,其中第i本书的价格是Ci,对于第j门算法的提升程度是Ai,j。
判断能否满足小明的期望,如果可以的话,最少花费为?
思路:
因为m、n的数目也不大,所以我们可以枚举每种选择方案逐一判断。
AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

int n,m,x;
int cost[15],a[15][15],chose[15];
//chose[]存放选择的书的行数 (下标) 
bool st[15];
//st[]标记该本书是否被选择 
int ans=0x3f3f3f3f;	//一个非常大的数,1e9左右 

//从第u本书开始决定是否选择
//遍历选择end本书的每种情况 
void dfs(int u,int end)	//dfs深度优先 
{
	//当u=end+1时,说明已经选取了end本书
	//那么此时我们可以判断该种选择方案是否可行
	//可行计算一下花费并更新答案 
	if(u==end+1)
	{
		bool flag=0;	//记录方案是否可行 
		
		//遍历每一列 
		for(int i=1;i<=m;i++)
		{
			int now_s=0;	//记录每一列可以提高的level总和
			 
			//遍历选择的每本书 
			for(int j=1;j<=end;j++)
				now_s+=a[chose[j]][i];
			//如果某本书没有被选择,那么它的下标一定没被存进chose中去 
				
			if(now_s<x)	//如果该列可以提高的技能水平小于x 
			{
				flag=1;	//那么该方案一定不可行,不需要更新花费 
				break;
			}
		}
		
		if(flag==0)	//如果该方案可行 
		{
			int sum=0;	//记录该方案的花费总和 
			for(int i=1;i<=end;i++)
				sum+=cost[chose[i]];
			ans=min(sum,ans);
		}
		return ;	//结束 
	}
	
	//从第u本书开始选择 
	for(int i=u;i<=n;i++)
	{
		if(st[i]==0)	//如果该本书没被选择 
		{
			st[i]=1;
			chose[u]=i;	//记录当前选的书 
			dfs(u+1,end);	//进入该本书被选择的下一层 
			st[i]=0;	//恢复状态, 表示该本书没被选,让for循环继续 
		}
	}
}

/*
1234
dfs(1,2) 
12 13 14
23 24
34
*/

int main()
{
	//读入数据 
	scanf("%d%d%d",&n,&m,&x);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&cost[i]);
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	}
	
	//因为n、m最大才为12,所以我们可以 
	//枚举每个长度下的状况 
	for(int i=1;i<=n;i++)
		dfs(1,i);
	
	if(ans==0x3f3f3f3f)	printf("-1\n");	//如果答案没有被更新 
	else	printf("%d\n",ans);
	return 0;
}

D - 一只小蜜蜂…(递推) HDU - 2044

有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。
在这里插入图片描述
Input
输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0<a<b<50)。
Output
对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。
思路:
从a到b的可能路线数与从1到b-a+1的可能路线数相同。
AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
int n;
ll fun[55];
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		memset(fun,0,sizeof(fun));
		int a,b;
		scanf("%d%d",&a,&b);
		b=b-a+1;
		fun[2]=1,fun[3]=2;
		for(int i=4;i<=b;i++)
		{
			fun[i]=fun[i-1]+fun[i-2];
		}
		printf("%lld\n",fun[b]);
	}
	return 0;
}

E - 母牛的故事 (递推)HDU - 2018

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
Input
输入数据由多个测试实例组成,每个测试实例占一行,包括一个整数n(0<n<55),n的含义如题目中描述。
n=0表示输入数据的结束,不做处理。
Output
对于每个测试实例,输出在第n年的时候母牛的数量。
每个输出占一行。
思路:
今年🐂的数量=去年🐂的数量+今年新生🐂的数量=去年🐂的数量+三年前🐂的数量
在这里插入图片描述

AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e8;
int a[N],n;
int main()
{	
	a[1]=1;
	a[2]=2;
	a[3]=3;
	for(int i=4;i<55;i++)
	{
		a[i]=a[i-1]+a[i-3];	//今年牛的数量 = 去年牛的数量 + 去年的牛在今年生的小牛的数量 
	}						//			   = 去年牛的数量 + 三年前牛的数量 
	while(scanf("%d",&n)&&n)
	{
		printf("%d\n",a[n]);
	}
	return 0;
}

F - 阿牛的EOF牛肉串(递推) HDU - 2047

今年的ACM暑期集训队一共有18人,分为6支队伍。其中有一个叫做EOF的队伍,由04级的阿牛、XC以及05级的COY组成。在共同的集训生活中,大家建立了深厚的友谊,阿牛准备做点什么来纪念这段激情燃烧的岁月,想了一想,阿牛从家里拿来了一块上等的牛肉干,准备在上面刻下一个长度为n的只由"E" “O” "F"三种字符组成的字符串(可以只有其中一种或两种字符,但绝对不能有其他字符),阿牛同时禁止在串中出现O相邻的情况,他认为,"OO"看起来就像发怒的眼睛,效果不好。
你,NEW ACMer,EOF的崇拜者,能帮阿牛算一下一共有多少种满足要求的不同的字符串吗?
PS: 阿牛还有一个小秘密,就是准备把这个刻有 EOF的牛肉干,作为神秘礼物献给杭电五十周年校庆,可以想象,当校长接过这块牛肉干的时候该有多高兴!这里,请允许我代表杭电的ACMer向阿牛表示感谢!
再次感谢!
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数n组成,(0<n<40)。
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。
思路:
一开始还以为是道全排列的题…
这道题n最大也才是40,我们就直接找规律递推把每一个长度的涂法数量存起来就行了。

n=1,a[1]=3;
n=2,a[2]=3*3-1=8;
n=3,a[3]=3*8-2=22;
a[n]=a[n-1]*3-a[n-3]*2;

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
long long a[45];
int main()
{
	int n;
	a[1]=3;
	a[2]=8;		//=3*3-1=8
	a[3]=22;	//=3*3*3-2*2-1=27-4-1=22
	for(int i=4;i<40;i++)
		a[i]=3*a[i-1]-2*a[i-3];
	//新增一个字符后涂法数量
	//=未增前的涂法数量*3(E/O/F)-OO连续的涂法数量
	//新增后oo连续==倒数一二都为o==倒数三为E/F ==倒数四为E/O/F 
	while(cin>>n)
		cout<<a[n]<<endl;
	return 0;
}

H - 排列2(全排列) HDU - 1716

Ray又对数字的列产生了兴趣:
现有四张卡片,用这四张卡片能排列出很多不同的4位数,要求按从小到大的顺序输出这些4位数。
Input
每组数据占一行,代表四张卡片上的数字(0<=数字<=9),如果四张卡片都是0,则输入结束。
Output
对每组卡片按从小到大的顺序输出所有能由这四张卡片组成的4位数,千位数字相同的在同一行,同一行中每个四位数间用空格分隔。
每组输出数据间空一行,最后一组数据后面没有空行。
Sample Input
1 2 3 4
1 1 2 3
0 1 2 3
0 0 0 0
Sample Output
1234 1243 1324 1342 1423 1432
2134 2143 2314 2341 2413 2431
3124 3142 3214 3241 3412 3421
4123 4132 4213 4231 4312 4321

1123 1132 1213 1231 1312 1321
2113 2131 2311
3112 3121 3211

1023 1032 1203 1230 1302 1320
2013 2031 2103 2130 2301 2310
3012 3021 3102 3120 3201 3210
这道题就是输出格式非常🤢🤮
AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
	int a[5];
	int vis2=1;
	while(scanf("%d%d%d%d",&a[1],&a[2],&a[3],&a[4]))
	{
		if(a[1]==0&&a[2]==0&&a[3]==0&&a[4]==0)	break;
		sort(a+1,a+5);
		if(vis2==0)	printf("\n");
		vis2=0;
		int vis1=1,temp;
		do
		{
			if(a[1]==0)	continue;
			if(temp!=a[1]&&vis1==0)	
			{
				printf("\n");
				vis1=1;
			}
			if(vis1==0)	printf(" ");
			for(int i=1;i<5;i++)
			{
				printf("%d",a[i]);
				vis1=0;
			}	
			temp=a[1];	//记录千位数字
		}while(next_permutation(a+1,a+5));
		printf("\n");
	}
	return 0;
}

I - Ignatius and the Princess II (全排列)HDU - 1027

Now our hero finds the door to the BEelzebub feng5166. He opens the door and finds feng5166 is about to kill our pretty Princess. But now the BEelzebub has to beat our hero first. feng5166 says, “I have three question for you, if you can work them out, I will release the Princess, or you will be my dinner, too.” Ignatius says confidently, “OK, at last, I will save the Princess.”

“Now I will show you the first problem.” feng5166 says, “Given a sequence of number 1 to N, we define that 1,2,3…N-1,N is the smallest sequence among all the sequence which can be composed with number 1 to N(each number can be and should be use only once in this problem). So it’s easy to see the second smallest sequence is 1,2,3…N,N-1. Now I will give you two numbers, N and M. You should tell me the Mth smallest sequence which is composed with number 1 to N. It’s easy, isn’t is? Hahahahaha…”
Can you help Ignatius to solve this problem?
Input
The input contains several test cases. Each test case consists of two numbers, N and M(1<=N<=1000, 1<=M<=10000). You may assume that there is always a sequence satisfied the BEelzebub’s demand. The input is terminated by the end of file.
Output
For each test case, you only have to output the sequence satisfied the BEelzebub’s demand. When output a sequence, you should print a space between two numbers, but do not output any spaces after the last number.
Sample Input
6 4
11 8
Sample Output
1 2 3 5 6 4
1 2 3 4 5 6 7 9 8 11 10
AC代码:

//一道典型的模板题,直接套板子就行了
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[1010];
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=1;i<=n;i++)	a[i]=i;
		int cnt=0;
		do
		{
			cnt++;
			if(cnt==m)	break;
		}while(next_permutation(a+1,a+n+1));
		printf("%d",a[1]);
		for(int i=2;i<=n;i++)
			printf(" %d",a[i]);
		printf("\n");
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值