洛谷入门 模拟与高精度

题目一:
P1042 乒乓球
走这
问题分析:

  1. 题目的思路很清晰了,就是在不同规则下寻找WL的个数,找到就+1,然后在不同规则下判断是否获胜
  2. 这里要注意的是比分有可能超过11或者21,所以判断条件不能是==11||21,这点卡了我好久= =
    正确代码:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string s[100005];
void score21(string s)
{
	int win = 0;
	int lose = 0;
	//	Game g;
	int pos = s.find('E');
	if(pos==-1)
	{
		pos = s.size();
	 } 
	for (int i = 0; i < pos; i++)
	{
		if (s[i] == 'W')
		{
			win++;
		}
		else if(s[i] == 'L')
		{
			lose++;
		}
		if ((win >= 21 || lose >= 21) && abs(win - lose) >= 2)
		{
			cout << win << ":" << lose << endl;
			win = 0;
			lose = 0;
		}
	}
	cout << win << ":" << lose << endl;
}
void score11(string s)
{
	int win = 0;
	int lose = 0;
	//	Game g;
	int pos = s.find('E');
	if(pos==-1)
	{
		pos = s.size();
	} 
	for (int i = 0; i < pos; i++)
	{
		if (s[i] == 'W')
		{
			win++;
		}
		else if (s[i] == 'L')
		{
			lose++;
		}
		if ((win >= 11 || lose >= 11) && abs(win - lose) >= 2)//比分可能会大于11 
		{
			cout << win << ":" << lose << endl;
			win = 0;
			lose = 0;
		}
	}
	cout << win << ":" << lose << endl;
}

int main()
{
	int i = 0;
	string sm;
	while (cin >> s[i])
	{
	//	getchar();
		sm += s[i];
		i++;
	}
	score11(sm);
	cout<<endl;
	score21(sm); 
}

问题二
P1563 玩具谜题
走这
问题分析:

  1. 这道题的思路也很清晰,判断小人方向再计数取余即可
  2. 但这里要注意的是,如果要用到取余,下标最好从0开始,这样就不会出现下标1开始能取到0的情况(被这个点卡住了= = )

正确代码

#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstdio>
using namespace std;
/*取余数组从下标0开始*/ 
map<string,int> m;
struct Solider{
	int dir;
	string work;
};
Solider s[10000000];
int main()
{
	int n,m;
	cin>>n>>m;
	string work;
	int id;
	string num;
	int n1;
	int n2;
	for(int i=0;i<n;i++)//取余数最好从0开始 
	{
		cin>>id>>work;
		s[i].dir = id;
		s[i].work = work;
	}
	int start = 0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d",&n1,&n2);
		if(n1==0)
		{
		  if(s[start].dir==1)
	     	{
	    	start = (start+n2)%n;//有没有可能取到0 
	     	}
	      else{
	    	start = (n-n2+start)%n;
	    	}
	    }
	    else{
	    	if(s[start].dir==1)
	    	{
	    		start=(n-n2+start)%n;
			}
			else{
				start = (start+n2)%n;
			}
		}
    } 
    cout<<s[start].work;
}

问题三:
P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two
走这

错误代码:
这题写到中途压根没写出来,本来是想将牛移动和人移动分别封装成一个方法,但是不知道怎么处理转向和移动的时间问题,现在想来是牛移动后人移动,再让时间++,还有就是怎样判断遇不到。
翻了大佬的代码,发现可以专属值,这样就将遇不到的问题转化为数字重复问题(我真的太菜了!!!)

正确代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
char map[15][15];
//设定数组存储牛和农夫的方向和坐标 
int cow[3];
int farmer[3];
int direct[4] ={1,2,3,4};//依次表示向上、下、左、右 ,不能-1,1,-1,1的设置,因为这样上和左的判断条件相同 
bool flag[100000000];//专属值的公式很大,所以必须一定要开大数组 
int special;
void mov(int x,int y,int dir,int kind)//x、y表示坐标,dir表示方向,kind表示行走的是哪个物种 0是牛,1是农夫 
{
	if(dir==direct[0])//向上
	{
		if(map[x-1][y]=='*')//转变方向也是移动 
		{
	//		dir = direct[3];
			if(kind==0)
			{
				cow[0] = direct[3];
			}
			else{
				farmer[0] = direct[3];
			}
		}
		else{
			x+=-1;//每分钟移动一次 
			//不更新坐标就陷入死循环
			if(kind==0)
			{
				cow[1] = x;
			 } 
			 else{
			 	farmer[1] = x;
			 }
		}
	}
	else if(dir==direct[1])//向下
	{
		if(map[x+1][y]=='*')//转变方向也是移动 
		{
	//		dir = direct[3];
			if(kind==0)
			{
				cow[0] = direct[2];
			}
			else{
				farmer[0] = direct[2];
			}
		}
		else{
			x+=1;//每分钟移动一次 
			if(kind==0)
			{
				cow[1] = x;
			 } 
			 else{
			 	farmer[1] = x;
			 }
		}
	}
	else if(dir==direct[2]) //向左 
	{
		if(map[x][y-1]=='*')//转变方向也是移动 
		{
	//		dir = direct[3];
			if(kind==0)
			{
				cow[0] = direct[0];
			}
			else{
				farmer[0] = direct[0];
			}
		}
		else{
			y+=-1;//每分钟移动一次 
			if(kind==0)
			{
				cow[2] = y;
			 } 
			 else{
			 	farmer[2] = y;
			 }
		}
	}
	else{
		if(map[x][y+1]=='*')//转变方向也是移动 
		{
	//		dir = direct[3];
			if(kind==0)
			{
				cow[0] = direct[1];
			}
			else{
				farmer[0] = direct[1];
			}
		}
		else{
			y+=1;//每分钟移动一次 
			if(kind==0)
			{
				cow[2] = y;
			 } 
			 else{
			 	farmer[2] = y;
			 }
		}
	}
}

int main()
{
	cow[0] = 1;
	farmer[0] = 1;//初始化牛与农夫的方向为向下 
	for(int i=0;i<12;i++)
	{
		map[i][0] = '*';
		map[i][11] = '*';
	}
	for(int j=0;j<12;j++)
	{
		map[0][j] = '*';
		map[11][j] = '*';
	}
	/*以上是给地图的边框加上障碍物*/
	for(int i=1;i<11;i++)
	{
		for(int j=1;j<11;j++)
		{
			cin>>map[i][j];/*初始化地图*/
			if(map[i][j]=='F')
			{
				farmer[1] = i;
				farmer[2] = j;//存储农夫的横纵坐标 
			}
			else if(map[i][j]=='C')
			{
				cow[1] = i;
				cow[2] = j;
			}
		}
	}
	int min = 0;
	while(true)
	{
		min++;
		mov(cow[1],cow[2],cow[0],0);
		mov(farmer[1],farmer[2],farmer[0],1);
		special = cow[1]*10000+cow[2]*100000+farmer[1]*100+farmer[2]*1000+cow[0]+farmer[0]*40;
		if(cow[1]==farmer[1]&&cow[2]==farmer[2])
		{
			cout<<min;
			break;
		}
		/*可以利用生成专属值的方法来判断是否陷入死循环*/
		if(flag[special]==true)//bool默认为false 
		{
			cout<<"0";
			break;
		}
		flag[special] = true;
	}
	
} 

代码分析:

  1. 大佬的代码将人牛移动封装成一个方法,通过一个变量用数字表示物种

  2. 同样也是将方向数字化,这样即可判断往哪移动

  3. 牛、人都含有方向、坐标的成员变量,这样更便利是移动人或者牛(将自己要用的属性封装起来)

  4. 用专属值判断是否陷入死循环

问题四:
多项式输出

AC代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	bool flag[110];
	memset(flag,0,sizeof(flag));
	for(int i=n;i>=0;i--)
	{
		int t;
		cin>>t;
		if(t==0&&i!=0)
		{
			continue;
		}
		if(i==n)//首项 
		{
			flag[i] = true;
			if(t>0&&t!=1)
			{
				cout<<t;
			}
			else if(t<0)
			{
				 cout<<"-";
				 if(abs(t)!=1)
				 {
				 	cout<<abs(t);//不加绝对值就输出两个负数 
			     }
			} 
		}
		if(i!=n&&i!=0){//次项 
			if(t>0)
			{//1和其他 
			    if(flag[n])
			    {
			    	cout<<"+";
				}
				flag[n] = true;//如果次首项不需要符号,其他需要 
				if(t!=1)
				{
					cout<<t;
				}
			}else{//-1和其他 
				
			    cout<<"-";//负数与+不同 
				if(t!=-1)
				{
					cout<<abs(t);
				}
			}
		} 
		if(i!=0)//非尾项的项数 
		{
			flag[0] = true;//如果非尾项正常输出,说明尾项需要+- 
			if(i!=1)
			{
				cout<<"x"<<"^"<<i;//输出项 
			}
			else{
				cout<<"x";
			}
		}
		if(i==0)//尾项 
		{
			if(t>0&&flag[0])
			{
				cout<<"+";//正 负 
			}
			if((!flag[0])||(flag[0]&&t!=0))
			{
				cout<<t;
			}
		}
	}
}

问题五:
麦森数
走这

问题分析:

  1. 错误代码就不贴了,就是一位一位算出来结果显示TLE,看了题解发现有个神奇的公式,利用对数求位数
    通过10的次方与位数关系即可简化一位一位求阶乘

  2. 在将2的次方-1时还判断了是否考虑进位,但实际是不用考虑的,2的次方不可能有0

看了大佬的代码是用了压位,但是还有快速幂的方法,目前还没看,以后待补
自己先按理解的压位仿写
正确代码

#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
/*压位求解*/
long long nums[510];
long long Pow2(int a)
{
	long long res = 1;
	for(int i = 1;i<=a;i++)
	{
		res*=2;
	}
	return res;
} 
int main()
{
	int n;
	cin>>n;
	int bits = ceil(n*log10(2));//利用对数求位数 
	cout<<bits<<endl;
	//一个数组元素存储2^32,然后再一个个分解到每一个数组元素
	long long tmp = Pow2(32);//存储2^32次方
	int counts = n/32;//P里面一共有counts个2^32
	int rest = n%32;//还剩rest个
	nums[1] = 1;//低位赋1 
	for(int i=1;i<=counts;i++)
	{
	   for(int j=1;j<=500;j++)
	  {
		 nums[j]*=tmp;//每个先保存tmp ,每一位乘tmp 
//		 if(nums[j]==0)当乘的次数多的时候,就会有某位为0而下一位不为0的 
//	    { 
//			break;
//		}     
	  }
	  for(int m=1;m<=500;m++)//处理进位 
	 {
		nums[m+1] += nums[m]/10;//最后每一个数组存储一位,这样500个元素也便利处理 
		nums[m] = nums[m]%10;
//		if(nums[i+1]==0&&nums[i]==0)存在相邻两位为0的状况 
//		{
//			break;
//		}
     }
	}
	/*处理剩下的k次2相乘*/
	for(int i=1;i<=rest;i++)
	{
		for(int j=1;j<=500;j++)
		{
			nums[j]*=2; 
		}
		for(int j=1;j<=500;j++)
		{
			nums[j+1] += nums[j]/10;
			nums[j]%=10;//先乘再处理进位问题 
		}
	} 
	nums[1]--;//2的倍数个位不可能 有0,所以一定不要借位
	int counts1 = 0;
	for(int i=500;i>=1;i--)
	{
		cout<<nums[i];//1-500有500个数 
		counts1++;
		if(counts1%50==0)//倒叙输出和正序输出不同,500-450有51个数 
		{
			cout<<endl;
		}
	} 
}

代码分析:

  1. 与阶乘不同的是,这个高精乘法没有即时进位,是先一位位乘再进位,这种方法适用于乘相同的数,阶乘应该也可
  2. 最后输出的时候,因为是从高位输出,所以不能用i%50的方法

问题六
P1786 帮贡排序
走这
问题分析:

  1. 看题目感觉就是利用sort自定义排序,但是sort会改变原来相等元素的相对位置,自己写的代码一片飘红,并且要注意的是在帮贡排序后,原来的相对顺序已经改变了,所以不能直接对职位和等级排序
  2. 并且自己的代码写的相当麻烦,创建了一个新数组存储原来的相对顺序,但还是只有30

错误代码

#include <iostream>
#include <algorithm>
#define Max 150
using namespace std;
int BangZhu = 1;
int FuBangZhu = 2;
int HuFa = 2;
int ZhangLao = 4;
int TangZhu = 7;
int JingYing = 25;
int BangZhong = Max;
string Work[8] = {"BangZhu","FuBangZhu","HuFa","ZhangLao","TangZhu","JingYing","BangZhong"};
struct People{
	string name;
	string work;
	long long devote;
	int level;
};
int n;
People nums[150];
People nums2[150]; 
int Find(string s)
{
	for(int i=0;i<8;i++)
	{
		if(Work[i]==s)
		{
			return i;
		}
	}
	return Max;
}
void Assignname(People* nums1,People* nums2)
{
	for(int i=1;i<=n;i++)
	{
		nums2[i].name = nums1[i].name;
	}
}
void Assign(People* nums1,People* nums2)
{
	for(int i=1;i<=n;i++)
	{
	//nums1经过帮贡排序后原来的顺序已经改了	nums2[i].work = nums1[i].work;
	    for(int j=1;j<=n;j++)//遍历寻找原来相同的人 
	    {
	    	if(nums1[i].name==nums2[j].name)
	    	{
	    		nums2[j].work = nums1[i].work;//找到名字相同的人给他职位 
	    		nums2[j].devote = nums1[i].devote;
	    		nums2[j].level = nums1[i].level;
			}
		}
	}
}
struct sort1{
	bool operator()(People s,People v)
	{
		if(s.devote>v.devote)//sort如果想要原来的相对顺序不改变,就不要加= 
		{
			return true;
		}
		else{
			return false;
		}
	}
};
struct sort2{
	bool operator()(People s,People v)
	{
		if(Find(s.work)!=Find(v.work))
		{
			if(Find(s.work)<Find(v.work))
			{
				return true;
			}
			else{
				return false;
			}
		}
		else
		{
			if(s.level>v.level)
			{
				return true;
			}
			else{
				return false;
			}
		}
	}
};
int main()
{
	cin>>n;
	
	for(int i=1;i<=n;i++)
	{
		cin>>nums[i].name>>nums[i].work>>nums[i].devote>>nums[i].level;
	}
	Assignname(nums,nums2);
	sort(nums+1,nums+n+1,sort1()); 
	for(int i=1;i<=n;i++)
	{
		if(nums[i].work!="BangZhu"&&nums[i].work!="FuBangZhu")
		{
			if(HuFa!=0)
			{
				nums[i].work = "HuFa";
				HuFa--; 
			    continue;
			}
			if(ZhangLao!=0)
			{
				nums[i].work = "ZhangLao";
				ZhangLao--;
				continue;
			}
			if(TangZhu!=0)
			{
				nums[i].work = "TangZhu";
				TangZhu--;
				continue;
			}
			if(JingYing!=0)
			{
				nums[i].work = "JingYing";
				JingYing--;
				continue;
			}
			if(BangZhong!=0)
			{
				nums[i].work = "BangZhong";
				BangZhong--;
				continue;
			}
		}
    }
    Assign(nums,nums2); //赋予新职位后,再将新数组排序,这样就不会改变原来的 
    /*赋予新职位后,原来是按帮贡排序,以至于原来的相对顺序改变了*/
	sort(nums2+1,nums2+n+1,sort2()); 
	for(int i=1;i<=n;i++)
	{
		cout<<nums2[i].name<<" "<<nums2[i].work<<" "<<nums2[i].level<<endl;
	}
} 

正确又便利的做法是在结构体记录原来的数组下标这样就不会改变相对顺序,但是不知道sort的稳定排序是否可以

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值