51nod 40 序列分解 暴力dfs or 折半+字典树

题意:长度为n的序列a,从a中选出两个长度为n/2的子序列b,c.a中每个元素要么属于b要么属于c.
2<=n<=40 -1e9<=a[i]<=1e9 问是否存在子序列b等于子序列c?


初始序列b,c都为空 假如a[1]属于序列b. 那么下一个x=a[1]的数可能属于序列c 中间的数肯定都压入序列b中
若序列b长度大于n/2,无解. 
14
1 7 10 1 7 4 5 7 1 10 7 4 5 1
WAWAWA... 按上面算法来执行则无解 实际上有{1,7,10,7,4,5,1}第5个7选给了序列b.
暴力来匹配. TLE... 加个剪纸过了. 这题数据水.可以构造数据来卡主.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+5;
int T,n,a[N],b[N],c[N]; 
bool flag;
void dfs(int x,int now,int pb,int pc)
{
	if(pb>n/2||flag)
		return;
	if(x==n+1)
	{
		if(pc==n/2)
			flag=true;
		return;
	}
	if(a[x]==b[now]&&pc<pb)
		dfs(x+1,now+1,pb,pc+1);
	b[++pb]=a[x];
	dfs(x+1,now,pb,pc);
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		flag=false;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
	
		b[1]=a[1];
		dfs(2,1,1,0);
		puts(flag?"Good job!!":"What a pity!");
	}
	return 0;
}




折半枚举 前半段和后半段分别选出 A,B,C,D
若B为A的前缀,则把A剩下部分压入字典树.

若D为C的后缀,则把查询C是否能在字典树上匹配 若能,则得到一解:A+D=B+C.

写的半天 然后还TLE了。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=40;
int T,n,a[N],id[N],b[N],c[N]; 
int ch[N*20][20],sz;
bool num[N*20];
map<int,int> mp;
void init()
{
	memset(ch[0],0,sizeof(ch[0]));
	memset(num,0,sizeof(num));
	sz=1;
	mp.clear();
}
void insert(string s)
{
	int u=0;
	for(int i=0;s[i];i++)
	{
		int c=s[i]-'a';
		if(!ch[u][c])
		{
			memset(ch[sz],0,sizeof(ch[sz]));
			ch[u][c]=sz++;
		}
		u=ch[u][c];
	}	
	num[u]=1;
}
int find(string s)
{
	int u=0;
	for(int i=0;s[i];i++)
	{
		int c=s[i]-'a';
		if(!ch[u][c])
			return 0;
		u=ch[u][c];
	}
	return num[u]==1;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int num=0;
		init();
		bool flag=false;
		scanf("%d",&n);
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			if(!mp[a[i]])
				mp[a[i]]=num+1,id[i]=num++;
			else
				id[i]=mp[a[i]]-1;
		}
		if(num>n/2)
		{
			printf("%s\n","What a pity!");
			continue;
		}
		for(int i=0;i<(1<<(n/2));i++)
		{
			int pb=0,pc=0;
			for(int j=0;j<n/2;j++)
			{
				if((i>>j)&1)	
					b[pb++]=id[j];
				else	
					c[pc++]=id[j];	
			}	
			if(pb<pc)
				swap(b,c),swap(pb,pc);
			int j;
			for(j=0;j<pc;j++)
				if(b[j]!=c[j])
					break;
			if(j==pc)
			{
				string s="";
				for(;j<pb;j++)
					s+='a'+b[j];
				insert(s);
			}
		}
		for(int i=0;i<(1<<(n/2))&&flag==false;i++)
		{
			int pb=0,pc=0;
			for(int j=0;j<n/2;j++)
			{
				if((i>>j)&1)
					b[pb++]=id[n/2+j];
				else
					c[pc++]=id[n/2+j];
			}
			if(pb<pc)
				swap(b,c),swap(pb,pc);
			int j,k=pb-pc;
			for(j=0;j<pc;j++,k++)	
				if(b[k]!=c[j])
					break;
			if(j==pc)
			{
				string s="";
				for(j=0;j<pb-pc;j++)
					s+='a'+b[j];
				if(find(s))
					flag=true;
			}
		}
		if(flag)
			printf("%s\n","Good job!!");
		else
			printf("%s\n","What a pity!");
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值