51nod 1400 序列分解【Dfs+剪枝】好题~

基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题

小刀和大刀是双胞胎兄弟。今天他们玩一个有意思的游戏。 大刀给小刀准备了一个长度为n的整数序列。小刀试着把这个序列分解成两个长度为n/2的子序列。

这两个子序列必须满足以下两个条件:

1.他们不能相互重叠。

2.他们要完全一样。

如果小刀可以分解成功,大刀会给小刀一些糖果。

然而这个问题对于小刀来说太难了。他想请你来帮忙。


Input
第一行给出一个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)
Output
如果小刀可以完成游戏,输出"Good job!!" (不包含引号),否则 输出"What a pity!" (不包含引号)。
Input示例
2
4
1 1 2 2
6
1 2 3 4 5 6
Output示例
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");
    }
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值