UOJ501 【JOISC2020】建筑装饰 4

UOJ501 【JOISC2020】建筑装饰 4

动态规划

先给出一种巨佬指导的乱搞,对序列进行二分,序列左边尽量使用\(A\),右侧尽量使用\(B\),然后凑出\(n\),如果没有答案,序列左边尽量使用\(B\),右侧尽量使用\(A\),再添加一些调整就直接通过了本题。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000005
using namespace std;
const int INF=1000000007;
int n,a[N],b[N],c[N];
int ans[N],dp[N][2],pre[N][2];
bool flag;
bool ok(int x)
{
    return x!=-1 && x!=INF;
}
void ckmax(int &x,int y)
{
    x=(x>y)?x:y;
}
void ckmin(int &x,int y)
{
    x=(x<y)?x:y;
}
bool fcheck()
{
    int mn=min(a[1],b[1]);
    for (int i=2;i<=n;++i)
    {
        if (mn<=min(a[i],b[i]))
            mn=min(a[i],b[i]); else
        if (mn>max(a[i],b[i]))
            return false; else
            mn=max(a[i],b[i]);
    }
    return true;
}
int check(int mid)
{
    dp[1][0]=1,dp[1][1]=0;
    for (int i=2;i<=n;++i)
    {
        if (i<=mid)
        {
            dp[i][0]=dp[i][1]=-1;
            if (a[i-1]<=a[i] && ok(dp[i-1][0]))
                ckmax(dp[i][0],dp[i-1][0]+1);
            if (b[i-1]<=a[i] && ok(dp[i-1][1]))
                ckmax(dp[i][0],dp[i-1][1]+1);
            if (a[i-1]<=b[i] && ok(dp[i-1][0]))
                ckmax(dp[i][1],dp[i-1][0]);
            if (b[i-1]<=b[i] && ok(dp[i-1][1]))
                ckmax(dp[i][1],dp[i-1][1]);
        } else
        {
            dp[i][0]=dp[i][1]=INF;
            if (a[i-1]<=a[i] && ok(dp[i-1][0]))
                ckmin(dp[i][0],dp[i-1][0]+1);
            if (b[i-1]<=a[i] && ok(dp[i-1][1]))
                ckmin(dp[i][0],dp[i-1][1]+1);
            if (a[i-1]<=b[i] && ok(dp[i-1][0]))
                ckmin(dp[i][1],dp[i-1][0]);
            if (b[i-1]<=b[i] && ok(dp[i-1][1]))
                ckmin(dp[i][1],dp[i-1][1]);
        }
    }
    if (mid>n)
        return max(dp[n][0],dp[n][1]);
    return min(dp[n][0],dp[n][1]);
}
void cmax(int &x,int &_x,int y,int _y)
{
    if (x<y)
        x=y,_x=_y;
}
void cmin(int &x,int &_x,int y,int _y)
{
    if (x>y)
        x=y,_x=_y;
}
void calc(int mid)
{
    dp[1][0]=1,dp[1][1]=0;
    for (int i=2;i<=n;++i)
    {
        if (i<=mid)
        {
            dp[i][0]=dp[i][1]=-1;
            if (a[i-1]<=a[i] && ok(dp[i-1][0]))
                cmax(dp[i][0],pre[i][0],dp[i-1][0]+1,0);
            if (b[i-1]<=a[i] && ok(dp[i-1][1]))
                cmax(dp[i][0],pre[i][0],dp[i-1][1]+1,1);
            if (a[i-1]<=b[i] && ok(dp[i-1][0]))
                cmax(dp[i][1],pre[i][1],dp[i-1][0],0);
            if (b[i-1]<=b[i] && ok(dp[i-1][1]))
                cmax(dp[i][1],pre[i][1],dp[i-1][1],1);
        } else
        {
            dp[i][0]=dp[i][1]=INF;
            if (a[i-1]<=a[i] && ok(dp[i-1][0]))
                cmin(dp[i][0],pre[i][0],dp[i-1][0]+1,0);
            if (b[i-1]<=a[i] && ok(dp[i-1][1]))
                cmin(dp[i][0],pre[i][0],dp[i-1][1]+1,1);
            if (a[i-1]<=b[i] && ok(dp[i-1][0]))
                cmin(dp[i][1],pre[i][1],dp[i-1][0],0);
            if (b[i-1]<=b[i] && ok(dp[i-1][1]))
                cmin(dp[i][1],pre[i][1],dp[i-1][1],1);
        }
    }
    int k;
    if (mid>n)
        k=max(dp[n][0],dp[n][1]); else
        k=min(dp[n][0],dp[n][1]);
    if (!ok(k))
        return;
    if (k==dp[n][0])
    {
        int x=n,_x=0;
        ans[x]=0;
        while (x!=1)
        {
            _x=pre[x][_x],--x;
            ans[x]=_x;
        }
    } else
    {
        int x=n,_x=1;
        ans[x]=1;
        while (x!=1)
        {
            _x=pre[x][_x],--x;
            ans[x]=_x;
        }
    }
    int del=k-(n >> 1);
    if (!del)
    {
        flag=true;
        return;
    }
    for (int i=1;i<=n;++i)
    {
        int nxt((i==n)?INF:(ans[i+1]?b[i+1]:a[i+1]));
        int pre((i==1)?-INF:(ans[i-1]?b[i-1]:a[i-1]));
        if (del>0 && !ans[i] && pre<=b[i] && b[i]<=nxt)
            ans[i]=1,--del;
        if (del<0 && ans[i] && pre<=a[i] && a[i]<=nxt)
            ans[i]=0,++del;
    }
    if (!del)
        flag=true;
}
int main()
{
    scanf("%d",&n),n <<=1;
    for (int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for (int i=1;i<=n;++i)
        scanf("%d",&b[i]);
    if (!fcheck())
    {
        puts("-1");
        return 0;
    }
    int l(1),r(n+1);
    while (l<=r)
    {
        int mid(l+r >> 1);
        int c(check(mid));
        if (c==(n >> 1))
        {
            calc(mid);
            for (int i=1;i<=n;++i)
                putchar(ans[i]?'B':'A');
            putchar('\n');
            return 0;
        }
        if (c<(n >> 1))
            l=mid+1; else
            r=mid-1;
    }
    for (int i=1;i<=n;++i)
        swap(a[i],b[i]);
    l=1,r=n+1;
    while (l<=r)
    {
        int mid(l+r >> 1);
        int c(check(mid));
        if (c==(n >> 1))
        {
            calc(mid);
            for (int i=1;i<=n;++i)
                putchar(ans[i]?'A':'B');
            putchar('\n');
            return 0;
        }
        if (c<(n >> 1))
            l=mid+1; else
            r=mid-1;
    }
    for (int i=1;i<=n;++i)
        swap(a[i],b[i]);
    for (int i=1;i<=n+1;++i)
    {
        calc(i);
        if (flag)
        {
            for (int j=1;j<=n;++j)
                putchar(ans[j]?'B':'A');
            putchar('\n');
            return 0;
        }
    }
    puts("-1");
    return 0;
}

正解:设\(dp_{i,0/1}\)表示第\(i\)个位置,选择\(A\)\(B\)的状态,满足条件的\(A\)的个数一定是一段区间,我们直接用一个\(pair\)类型表示可能选择\(A\)的个数的范围作为\(dp\)值进行转移即可。

posted @ 2021-04-13 19:50  GK0328  阅读(61)  评论(0编辑  收藏  举报