CF1381B Unmerge

CF1381B Unmerge

洛谷传送门

题目翻译

对于一个长度为\(2n\)的排列(排列是指一个没有重复元素,有顺序的正整数集合)(保证\(n\)为正整数),问:

是否可以把这个排列分解成两个长度为\(n\)的队列,使得每次取出两个队列的较小队首后,恰好能还原成原来的排列。如果可以,输出\(YES\),否则输出\(NO\)

输入格式

第一行包括一个整数\(t\quad(1\le t\le 1000)\),表示测试数据的组数。接下来的\(2t\)行为测试数据。

每组测试数据的第一行包含一个整数\(n\),意义如题所示。

每组测试数据的第二行包含一个长度为\(2n\)的排列\(p\)

数据保证:所有测试数据的\(n\)的和不超过\(2000\).

输出格式

对于每组数据,如果可以,输出\(YES\),否则输出\(NO\)

说明/提示

第一组数据,有:$[2,3,1,4]=merge([3,1],[2,4]) $。

第二组数据,我们可以看到\([3,1,2,4]\)为什么不能被合法分解。

第三组数据,有:\([3,2,6,1,5,7,8,4]=merge([3,2,8,4],[6,1,5,7])\)

第四组数据 ,有:\([1,2,3,4,5,6]=merge([1,3,6],[2,4,5])\)


自己翻译的,不知道能不能过。

一看到这种数列的题首先想到研究性质。手画几组样例发现,如果一个排列的最大值在这个排列的前\(n\)项出现,那么这个排列就肯定是废废了,因为这个最大值是出不来的,也就无法构造出这个排列。

然鹅这个性质好像并没有什么用处。这里仅作为思路展示来使用。

然后我们在进一步思考的时候,发现一个问题:对于某一个极大值来讲,从它开始到下一个比它大的值必须要在一个队列里连续排列。

就拿这组样例说话:

3 2 6 1 5 7 8 4

如果3在第一个队列里了,那么2肯定也在同一个队列里并且紧随其后,才可能保证3,2连续被弹出,否则,如果3在第一个队列,2在第二个队列,那么一开始弹出的就不是3而是2,如果2在6后面,在6没有被弹出之前也不可能轮到2,构造均宣布失败。

那么,这么一个数列就被我们分成了若干个小段,每段由一个段首极大值和在它后面的若干个较小值组成。比如上面的例子,最终我们就将其分成了如下的段落:

[3,2],[6,1,5],[7],[8,4]

到了这里就容易想到:如果我们能从这些长度不同的段落中挑出任意段,使之恰好能塞满长度\(n\)的一个排列,那么我们就构造成功了。(题目不需要我们考虑段与段之间的顺序问题)比如上面的样例答案就是:

[3,2,8,4],[6,1,5,7]

所以好像是个背包问题?

还是最容易的0/1背包。

所以我们只需要统计出所有的段落长度,然后来跑0/1背包即可。状态就定义为:\(dp[i][j]\)表示前\(i\)个物品能否装满容量为\(j\)的背包。一个到达性的背包问题。自然可以想到,如果\(dp[i-1][j]\)或者\(dp[i-1][j-len[i]]\)(其中\(len[i]\)表示第\(i\)段的长度)可以到达,那么\(dp[i][j]\)就是可以到达的。

答案就是\(dp[tot][n]\),可达就是\(YES\),否则就是\(NO\)

兴冲冲地写完代码,T了。(逃

后来一看,发现自己狂开了三个\(memset\)。(绝不能开,开了就T)仔细思考后,发现保存原数列的\(a\)数组和每段的长度\(len\)都不需要清空,反正也是被覆盖,没被覆盖的地方也用不上。不影响转移。

那么\(dp\)数组呢?如果依然\(memset\)或者手动清空的话还是会T啊。

考虑边转移边清空,我们只需要把初值\(dp[0][0]\)设置成1,之后转移之前把\(dp[i][j]\)先清掉,就能保证转移的有序正确性。

那么有了代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t,n,maxx,tmp,cnt,maxpos,tot;
int len[4010],a[4010];
int dp[4010][2010];//dp[i][j]表示前i个物品能否装满体积为j的背包。
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
    int x=0,f=1;
    char ch=nc();
    while(ch<'0'||ch>'9')
        if(ch=='-')
            f=-1,ch=nc();
    while(ch>='0'&&ch<='9')
        x=x*10+ch-48,ch=nc();
    return x*f;
}
int main()
{
    t=read();
    while(t--)
    {
        tot=0,maxx=0;
        n=read();
        for(int i=1;i<=2*n;i++)
        {
            a[i]=read();
            if(a[i]>maxx)
                maxx=a[i],maxpos=i;
            if(i==1)
                tmp=a[i],cnt=1;
            if(a[i]>tmp)
                len[++tot]=cnt,tmp=a[i],cnt=1;
            if(a[i]<tmp)
                cnt++;
        }
        if(maxpos<n)
        {
            puts("NO");
            continue;
        }
        len[++tot]=cnt;
        dp[0][0]=1;
        for(int i=1;i<=tot;i++)
            for(int j=0;j<=n;j++)
            {
                dp[i][j]=0;
                if(j>=len[i])
                    dp[i][j]|=dp[i-1][j-len[i]];
                dp[i][j]|=dp[i-1][j];
            }
        if(dp[tot][n])
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}
posted @ 2020-07-25 08:39  Seaway-Fu  阅读(219)  评论(0编辑  收藏  举报