算法---模拟(1)

目录

一、模拟的介绍

二、模拟题

(1)回文日期

(2)值周

(3)货物种类

(4)月月查华华的手机

(5)安卓图案解锁


一、模拟的介绍

关于模拟其实就是对于算法题目中的意思加以理解,然后慢慢退出解决题目的快速方法(这是我的个人理解),对于模拟类型的算法题,我认为想要快速掌握只能刷题,刷的越多越好,所以写这篇文章的目的我就是为了记录自己刷过的题,方便以后复习。

二、模拟题

(1)回文日期

题目链接:回文日期

个人题解:对于这题正常思路是从date1列举到date2,然后判断每个列举的日期是不是回文日期,看了下数据范围可能会超时,好像是列举到99999999吧。在这里我的思路是将date1和date2中的年份給单独划出,你想啊,如果是回文日期,那么在每一年中只有唯一的日子与它对应,所以说知道了年份,我们就可以推出一个回文日期,这样子,我们只需要列举year1到year2,最多列举到9999吧,年份题目说不超过4位。所以知道了年份之后我们推出关于年份的回文数,然后再判断这个回文数是不是回文日期就行了。

#include<bits/stdc++.h>
using namespace std;

int date_1,date_2;
int a,b,c,d;
bool reqi(int x)
{
	//判断日期是否符合规则
	int year=x/10000,month=x/100%10+x/1000%10*10,day=x%10+x/10%10*10;
	if(month<1||month>12||day<1||day>31){
		return false;
	}
	int f=0;//判断是否是闰年
	if(((year%4==0)&&(year%100!=0))||(year%400!=0)){
		//是闰年
		f=1;
	}
	if(month==1||month==3||month==5||month==7||month==8||month==10||month==12){
		if(day<=31&&day>=1){
			return true;
		}
		else{
			return false;
		}
	}
	else if(month==2){
		if(f==1){
			if(day<=29&&day>=1){
				return true;
			}
			else{
				return false;
			}
		}
		else{
			if(day<=28&&day>=1){
				return true;
			}
			else{
				return false;			
			}
		}
	}
	else{
		if(day<=30&&day>=1){
			return true;
		}
		else{
			return false;
		}
	}
}
int  main()
{
	int year1,year2;
	cin>>date_1>>date_2;
	year1=date_1/10000;
	year2=date_2/10000;
	int sum=0;
	for(int i=year1;i<=year2;i++){
		a=i%10;
		b=i/10%10;
		c=i/100%10;
		d=i/1000;
		int j=i*10000+a*1000+b*100+c*10+d;
		if(reqi(j)&&j>=date_1&&j<=date_2){
			sum++;
		}
	}
	cout<<sum;
	return 0;
}

(2)值周

题目链接值周

个人题解:这题如果数据弱的话我们可以用差分来做,当然这题还是可以用差分做的。我用的是贪心排序合并区间来做的。具体思路:输入的区间会不会是重复的,中间有一部分可能是重复的,那我们就将区间合并,下一个区间的左端点小于等于上一个区间的右端点表示这两个区间肯定是可以合并成一个区间的,注意别忘了等号,我当时就是忘了,想了很长时间呢。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef struct{
	int l,r;
}node;
struct rule{
	bool operator()(node x,node y){
		return x.l<y.l;
	}
};
node a[1000010];
int n,m;

int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++){
		cin>>a[i].l>>a[i].r;
	}
	sort(a,a+m+1,rule());
	int sum=n+1;//表示总长度
    int  l=a[0].l,r=a[0].r;
	for(int i=1;i<m;i++){		
		if(a[i].l<=r){
			//可以合并
		    //l不变,r变化,看谁最大
		    r=r>a[i].r?r:a[i].r;
		}
		else{
			sum=sum-(r-l+1);
			l=a[i].l;
			r=a[i].r;
			
		}
	}
	sum=sum-(r-l+1);
	cout<<sum;
}

(3)货物种类

题目链接:货物种类

对于该题,暴力法毫无疑问是会超时的,但你可以想一想,锻炼一下思路。我的做法是,输入的区间以及编号,我可以用结构体构造左端点,右端点,以及编号。然后排序,排序规则是编号小的在前面,编号相同的按照左端点小的在前面。然后开始模拟,如果有重复区间就合并,没有的话还需要在创建一个差分数组。到后面,差分数组我们怎么用呢?根据我们前面的排序规则,后面是不会有重复的,并且编号小的就是在前面,这样子我们就可以得到每个仓库的货物种类数。

#include<bits/stdc++.h>
using namespace std;

typedef struct{
	int l,r,d;
}node;
node a[1000010];
int n,m;
struct rule{
	bool operator()(node x,node y)
	{
		if(x.d!=y.d){
		  return x.d<y.d;
	    }
	    else{
	    	return x.l<y.l;
		}
	}
};
int d[1000010];

int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++){
		cin>>a[i].l>>a[i].r>>a[i].d;
	}
	sort(a,a+m,rule());
	int l=a[0].l,r=a[0].r;
	
	for(int i=1;i<m;i++){
		if(a[i].l<=r&&a[i].d==a[i-1].d){
			//可以合并
			r=max(r,a[i].r);
		}
		else{
			d[l]++;
			d[r+1]--;
			l=a[i].l;
			r=a[i].r;
		}
	}
	d[l]++;
	d[r+1]--;
    int sum=0,ans=1,ma=0;//sum表示的就是货物种类数,ans是位置
    for(int i=1;i<=n;i++){
    	sum+=d[i];
    	if(sum>ma){
    		ma=sum;
    		ans=i;
		}
	}
	cout<<ans;
	return 0;
}

 

(4)月月查华华的手机

题目链接:月月查华华的手机 

对于本题,题目虽然长,可是我们提炼一下,题目大致意思就是输入一个字符串,然后判断后面输入的字符串是不是它的子序列(注意子序列没有特殊说明的情况就是不连续,子串是连续的)。对于该题,我们该怎么做呢?那我们就要对于子序列的字符串拿它的字符一个一个比,比它第一次出现的位置,然后在比在这个字符后面第一次出现的位置,如果没有的话那就肯定不是子序列。那么这里我用了last数组表示的是在整个字符串中第一次出现的位置,它的维护是从后往前的,ne[i][j]数组表示的是在i位置后面第一次出现j的位置是什么(j=0表示是a,j=1表示是吧,以此类推)。

 

#include<bits/stdc++.h>
using namespace std;

int ne[1000010][40];
int last[40];
int n;
string s;

int main()
{
	cin>>s;
	int len=s.size();
	memset(last,-1,sizeof(last));
	for(int i=len-1;i>=0;i--){
		for(int j=0;j<26;j++){
			ne[i][j]=last[j];
		}
		last[s[i]-'a']=i;
	}
	cin>>n;
	while(n--){
		string s1;
		cin>>s1;
		len=s1.size();
		int f=0;
        //注意哈,pos不能写成ne[0][s[i]-'a']
        //解释一下,毕竟我就在这从出错了
        //ne[0][s[i]-'a']表示从0位置之后看,不包括0
        //可万一第一次出现的位置就是0了呢,所以
        //用的是last,last从后往前推出的,他表示的就是
        //在整个字符串中第一次出现字符的位置
		int pos=last[s1[0]-'a'];
		for(int i=1;i<len;i++){
			pos=ne[pos][s1[i]-'a'];
			if(pos==-1){
				f=1;
				break;
			}
		}
		if(f==1){
			cout<<"No"<<endl;
		}
		else{
			cout<<"Yes"<<endl;
		}
	}
	return 0;
}

(5)安卓图案解锁

题目链接:安卓图案解锁 

这题其实对于模拟有点难度(反正我觉得有点难)。对于连接成功的要求是数字吧不重复,这个简单,我们使用标记数组v数组来标记即可。第二点是连接两个数字中途是否经过其他数字,并且该数字是否使用,1, 9连接经过5;3,7连接经过5;1,3连接经过2,以此类推。

这里我用flag数组,相同则连接需要经过一个数,并且你会发现一个规律,假如连接a,b,则中间经过的数=(a+b)/2.比如说flag[1]=flag[3]说明1和3的连接经过(1+3)/2=2.然后我们就这样推着下去进行模拟。

#include<bits/stdc++.h>
using namespace std;

int v[10];
string s;
int flag[10]={5,1,2,1,4,0,4,1,2,1};
int main()
{
	while(cin>>s){
		int len=s.size();
        if(len>9||len<1){
            cout<<"NO"<<endl;
            continue;
        }
		memset(v,0,sizeof(v));
		int f=0;
		int t1,t2;
		for(int i=0;i<len;i++){
			t2=s[i]-'0';
			if(i==0){
				v[t2]=1;
				continue;
			}
			if(v[t2]==1){
				f=1;
				break;//不符合
			}
			t1=s[i-1]-'0';//上一个
			//现在判断两点连接中间有其他的点吗
			if(flag[t1]==flag[t2]){
				//中间有其他的点
				if(v[(t1+t2)/2]!=1){//中间的点是否使用
					f=1;
					break;
				}
				else{
					v[t2]=1;
				}
			}
			else{
				v[t2]=1;
			}
		}
		if(f==0){
			cout<<"YES"<<endl;
		}
		else{
			cout<<"NO"<<endl;
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜到极致就是渣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值