算法竞赛入门经典第二版 函数和递归

竞赛题目选讲

1,刽子手游戏:
在博客上一找,基本都是混子把书上的代码复制了一遍;
希望博客可以管理一下这些人,删除不用心的博客
思路:

1.输入2个字符串
2.每一个S2里面在s1里面找找看有没有一样的,没有chance就扣去一
3。因为猜过的数字不能再猜,用一个数组记录猜过的数字看看有没有重复
代码:


#include <iostream>
#include <string>
using namespace std;
int main()
{
    
    
	string m,n;
	int count = 0;
	while(cin>>count>>m>>n&&count != -1)
	{
    
    
		int chance=7,left=m.size();//chance 是能猜的机会,left是能猜的位置 
		for(int i=0;i<n.size();++i)
		{
    
    
			int ok=0;//一开始标记没猜对 
			for(int j=0;j<m.size();++j)
			{
    
    
				if(n[i]==m[j]) 
				{
    
    
					m[j]=' '; //在这个是精华 
					left--;
					ok=1;
				}//猜对了位置扣去一 ok=1;
			}
			if(ok==0) chance--;//没猜对减去一次机会 
			
		}	
		if(left <= 0) cout<<"you win"<<endl;
		else if(chance <= 0) cout<<"you lose"<<endl;
		else cout<<"you chickened out"<<endl;
	}
}

核心:

int ok=0;//一开始标记没猜对 
			if(ok==0) chance--;//没猜对减去一次机会 

这个写法就i是一个模板:
如果ok=0没猜对,标记没猜对的话机会就少了一次;

for(int j=0;j<m.size();++j)
			{
    
    
				if(n[i]==m[j]) 
				{
    
    
					m[j]=' '; //在这个是精华 
					left--;
					ok=1;
				}//猜对了位置扣去一 ok=1;
			}

n[i]==m[j] 如果猜对了,要猜的位置减去1,ok=1;
还有一个注意点:
猜过的数字,不能再猜;
有2方法:
a.把每次字符存下来,然后一次for循环,找到的话就chance;
b.把猜对的字符在s1里面全部改为空格
有2种情况
a.把猜对的元素全部改为“ ”空格,这样猜对的话就可以把所有猜过而且猜对的元素都改为空格;这样后面再输入这个字符的话就匹配不上去,相当于错了一次;
b.猜的没有在s1里面;
这样没猜对,和猜过都要扣去一次机会,没有必要
***技巧:如果一个东西不能重复就把之前猜过的一次,就把全部的元素替换,这样后续就匹配不到
2.
救济金发放;
m个人站在一个圈,官员a数k个停下来,官员b数m个停下来,选中的官员离开
官员a逆时针数,官员b顺时针数
思路:
/

1.输入n,k,m
2.模拟每次的操作,如果还有剩下就操作
3.如果遇到空格,遇到第一个非空格就才t–;
4.如果是最后一个的话就不用输出“,”

*/


#include <iostream>
using namespace std;
int a[10010],m,n,k;
int go(int p,int d,int t)
{
    
    
	while(t--)
	{
    
    
		do
		{
    
    
		 p=(p+d+n-1)%n+1;

		}while(a[p]==0);
	}
	return p;
}
int main()
{
    
    

	cin>>n>>k>>m;
	int left=n;
	int p1=n,p2=1;
	for(int i=1;i<=n;++i) a[i]=i; 
	while(left)//还剩下一些人 
	{
    
    
		 p1=go(p1,1,k);
		 p2=go(p2,-1,m);//p1,p2是要出队的人
		cout<<p1<<" ";//输出p1是谁
		left--;//出队一个人 
		if(p2!=p1) 
		{
    
    
			cout<<p2;
			left--;//如果p2和p1不是一个人继续出队 
		}
		a[p1]=a[p2]=0;
		if(left) cout<<",";//还有人的话就要输出,;
		 
	}
	cout<<endl;
	return 0;
 } 

核心:
1.利用一个变量来标记是否要输出逗号
这个技巧可以用来标记是否要输出空格;

if(left) cout<<",";

如果不是最后一个就输出逗号;
扩展:
**质数的分解。**题目私信我;

2.数过的元素,数过的数组改为空格
这个技巧和上面那一个技巧异曲同工之妙,,有的时候为了不去操作数组,改变数组就用这个技巧来防止重复计数;

	a[p1]=a[p2]=0;

数过的人把它改为空格,这样后面数到的话就跳过;
3.移动函数:

int go(int p,int d,int t)
{
    
    
	while(t--)
	{
    
    
		do
		{
    
    
		 p=(p+d+n-1)%n+1;

		}while(a[p]==0);
	}
	return p;
}

while(要需要移动的话)
{
那就移动到第一个非0的数字,因为0已经动过了,不能算

}
p=(p+d+n-1)%n;
这个是根据公式推演出来
一个是正向
p是当前位置,d是移动几步,n可以叫做数组偏移量,如果逆时针没有加上这个的话会出现负数%n是防止正的时候超过数组;
扩展约瑟夫环问题


#include <iostream>
using namespace std;
int a[10010],m,n,k;
int go(int p,int t)
{
    
    

		do
		{
    
    
			p=(p+t+n)%n;
		}while(a[p]==0);
	
	return p;
}
int main()
{
    
    

	cin>>n>>k;
	int left=n;
	int p1=1;
	for(int i=1;i<=n;++i) a[i]=i; 
	while(left) 
	{
    
    
		 p1=go(p1,k);
		cout<<p1<<" ";
		left--;//出队一个人 
	
		a[p1]=0;
		if(left!=1) cout<<",";//还有人的话就要输出,;
		 
	}
	cout<<endl;
	return 0;
 } 

思路:
每次移动步数,然后出队,把出队的地方改为0;
每次移动到第一个非0的数字;
模板总结:

int go(int p,int t)
{
    
    

		do
		{
    
    
			p=(p+t+n)%n;
		}while(a[p]==0);
	
	return p;
}

while(a[p]==0)/如果是0就跳过,找到第一个非0就输出;

猜你喜欢

转载自blog.csdn.net/m0_51373056/article/details/109399355