AcWing 4654消除游戏

这道题其实说思路的话很简单,但是纠结起来细节确实很让人头疼。

一、你可以直接用暴力解法,但是在那个判题系统里面,如果你用了暴力,可能会给你时间上的限制,在多字符输入的时候就会造成时间限制的问题,导致AC不了。作者在这个题上犯了这样的错误,总是想着暴力解,结果就是竹篮打水。下面的代码仅供参考,案例都是对的,但是对于很长的字符会通过不了,因为时间限制:

#include
#include
#include
#include 
#include
#include
#include
#include
#include
#include
#include
#define MAX 1000010
#define _for(i,a,b) for(int i=a;i<(b);i++)
using namespace std;
typedef long long LL;
char arr[MAX];
int cnt[MAX];
char b[MAX];
LL kuaisu(LL a,LL b){
	LL sum=1;
	while(b>0){
		if(b&1)
		sum*=a;
		a=a*a;
		b>>=1; 
	}
	return sum;
}
int main(){
	cin>>arr;
	LL count=1000000;
	while(count--){
	for(int i=0;i

如果你要看的话,注意几点:

这里用了fill_n的初始化方式,这个对于C++的容器大概都可以用,大家可以尝试以下,当然memset初始化数组也是可以的。这里的思路就是设置一个标记数组cnt,用来标记已经删除的元素,用b数组作为辅助数组,arr是输入字符的数组。

二、其实这个题可以用数据结构中的双链表构造,因为这个题目的要求就是对于某个字符的左右两边进行删除操作,而且有时候也会相当的频繁,所以就想到了用链表的方式来进行解题。而链表就又分为很多种,这里采用双链表会比较方便,因为涉及到前后驱的元素。

那么就讲讲思路:假如这些字符已经处于双链表当中了。对于这些字符来说,有两个判定条件,读者可以去回顾一下题目,就是当前元素和前驱元素相同,但是和后驱元素不同,这时候需要删除本元素和后驱元素,另一个就是当前元素和前驱元素不同,但是和后驱元素相同。

那么我们可以去从头开始,第一个元素可以肯定是不会被删除的,因为它没有前驱元素。所以在输入的时候从数组的下标1开始进行输入。接下来是重点,就是可以分为两个部分来进行讨论,即:备份元素,和待删除元素。

解释一下,备份元素指的是当前还未被删除的所有元素;待删除元素指的是已经通过题目条件判断可以删除的元素。我们不着急先删除元素,因为如果直接删除,会造成判断失误的情况,比如你在删除第一次后,剩下的元素也可能构成能删除的条件呀。从备份元素集合中过滤出可以删除的元素,把这些能够删除的元素放到待删除的集合当中,然后清空备份元素,对待删除元素进行处理。

删除元素的时候,你需要判断这些元素是否重复删除了,这关系着你进入待删除集合的时候是否重复删除了,所以还需要一个数组来判定状态,也就是下面代码中的st数组。当然,在删除的元素中,你还需要判断这个删除的元素的左右躯是否存在的问题,不存在那就不能操作,存在就进行删除操作,至于删除操作怎么进行的,可以参考下面代码的实现,这里用了双链表的数组表示,也就是用几个数组成功表示的,分别是输入字符的数组s,单个字符的左躯lefts,单个字符的右躯rights。OK,总体过程说个差不多,上代码。

#include
#include
#include
#include 
#include
#include
#include
#include
#include
#include
#include
#define MAX 1000010
#define _for(i,a,b) for(int i=a;i<(b);i++)
using namespace std;
char s[MAX];
int lefts[MAX],rights[MAX];
vectorq,w;
bool st[MAX];
int n;
void insert(int a){
	if(!st[a]){
		st[a]=true;
		w.push_back(a);
	}
}
void dels(){
	w.clear();
	for(int j:q){
		int a=lefts[j];
		int b=j;
		int c=rights[j];
		if(s[a]==s[b]&&s[b]!=s[c]&&s[c]!='#')
		{
			insert(b);
			insert(c);
		}
		else if(s[a]!=s[b]&&s[b]==s[c]&&s[a]!='#')
		{
			insert(a);
			insert(b);
		}
		
	}
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++){
		lefts[i]=i-1;
		rights[i]=i+1;
		q.push_back(i);
	}
	s[0]=s[n+1]='#';
	rights[0]=1;
	lefts[n+1]=n;
	while(true){
		dels();
		if(w.empty())break;
		q.clear();
		for(int k:w){
			int a=lefts[k];int b=k;int c=rights[k];
			if(!st[a]&&a&&(q.empty()||a!=q.back()))
			q.push_back(a);
			if(!st[c]&&c!=n+1)
			q.push_back(c);
			rights[a]=c;
			lefts[c]=a;
		}
		
	}
	if(rights[0]==n+1)
	puts("EMPTY");
	else
	{
		for(int i=rights[0];i!=n+1;i=rights[i]){
			printf("%c",s[i]);
		}
	}
	
	
	return 0;
}

dels函数就是用来让删除元素进入的,insert函数就是进行待删除元素进入的。

说的过程比较粗糙,如有不明白的地方欢迎私信交流。

你可能感兴趣的:(算法,c++,数据结构,链表)