CF1381B Unmerge

CF1381B Unmerge

洛谷传送门

题目翻译

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

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

输入格式

第一行包括一个整数(tquad(1le tle 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;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13375485.html