小刀和大刀是双胞胎兄弟。今天他们玩一个有意思的游戏。 大刀给小刀准备了一个长度为n的整数序列。小刀试着把这个序列分解成两个长度为n/2的子序列。
这两个子序列必须满足以下两个条件:
1.他们不能相互重叠。
2.他们要完全一样。
如果小刀可以分解成功,大刀会给小刀一些糖果。
然而这个问题对于小刀来说太难了。他想请你来帮忙。
第一行给出一个T,表示T组数据。(1<=T<=5) 接下来每一组数据,输入共2行。 第一行包含一个整数n (2<=n<=40且为偶数)。 第二行给出n个整数a[0],a[1],a[2],…,a[n-1]表示大刀给小刀准备的序列。(-1,000,000,000<=a[i]<=1,000,000,000)
如果小刀可以完成游戏,输出"Good job!!" (不包含引号),否则 输出"What a pity!" (不包含引号)。
2 4 1 1 2 2 6 1 2 3 4 5 6
Good job!! What a pity!
题目大意:
规定每一个数分到集合A或者是集合B,对应每个集合都要n/2个数,按照相对位子最终得到的集合A和集合B要完全一样输出Good job!!,否则输出What a pity!。
思路(一开始的思路除了暴力就是二分匹配,然而二分匹配还不会处理相对位子的限制,后来灵机一动想到剪枝好开森):
1、首先观察到n不大,最大可能达到40.那么最开始的想法就是暴力啦,2^40规定每个数对应需要分在的集合,最后暴力判断一波。但是显然超时。
2、那么接下来考虑其性质进行剪枝(a【i】表示的是原序列,A【i】表示的是得到的A集合):
①如果当前情况某个集合中的元素的个数大于n/2个,那么这种情况就没有必要继续Dfs下去了。
②如果已经找到了最终答案,也没有必要继续Dfs下去了。
③对应当前位子在第now个数,那么已经进行完操作分配的就有now-1个数,对应我们统计A集合中元素的个数假设此时为cont.那么如果当前这个数我们要分到B集合的话,对应需要判断:if(now-cont+1<=cont&&a[now]==A[now-cont])因为我们有一个相对位子的先后顺序要求,那么我们可以考虑先分配集合A,那么再之后分配集合B,所以如果此时B集合中的数较多的话,是不行的。
而且,因为此时A集合已经有了cont个数,那么对应当前这个分配到集合B中的元素需要和集合A中的第now-cont个元素相等才行。
这里就能去掉很多很多的操作。
我们从盲目分配的方案,变成了有目的性的想要得到可行分配得的方案。
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
#define inf 1000000060
int a[500];
int vis[500];
int n,flag;
void Dfs(int now,int cont)
{
//printf("%d %d\n",now,cont);
if(flag==1)return ;
if(cont>n/2)return ;
if(now==n&&cont==n/2)
{
flag=1;
return ;
}
if(now==n)return ;
vis[cont]=a[now];
Dfs(now+1,cont+1);
vis[now]=-inf;
if(now-cont+1<=cont&&a[now]==vis[now-cont])
{
Dfs(now+1,cont);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
flag=0;
for(int i=0;i<n;i++)
{
vis[i]=-inf;
}
Dfs(0,0);
if(flag==1)printf("Good job!!\n");
else printf("What a pity!\n");
}
}