AtCoder ARC097C Sorted and Sorted:dp

Leohh發表於2018-06-04

傳送門

題意

有 $ 2n $ 個球排成一行,其中恰好有 $ n $ 個白球和 $ n $ 個黑球。每個球上寫著數字,其中白球上的數字的並集為 $ \lbrace 1 \dots n\rbrace $ ,黑球上的數字的並集也為 $ \lbrace 1 \dots n\rbrace $ 。

你可以交換任意兩個相鄰的球若干次,以使得對於所有白球,數字大小從左到右遞增,黑球也是一樣。

問你最少的交換次數。$ (n \leq 2000) $

題解

如果所有球最後的位置 $ P(i) $ 已經確定,那麼最少交換次數 $ ans $ 為:
\[ ans = \sum_{i=1}^{2n} \sum_{j=i+1}^{2n} [P(i) > P(j)] \]
也就是相對位置改變了的球對 $ (i,j) $ 的個數。

然後考慮如何dp。

由於白球和黑球內部,數字大小遞增,所以可以考慮從左到右依次填球。

$ dp[i][j] $ 表示已經從左到右填了 $ i $ 個白球,$ j $ 個黑球,此時的最小代價。

則最終答案就是 $ dp[n][n] $

然後考慮如何轉移。

對於 $ dp[i][j] $ 來說,接下來要麼填一個白球,要麼填一個黑球。

設 $ costw[i][j] $ 表示已經填了 $ i $ 個白球,$ j $ 個黑球,該填第 $ i+1 $ 個白球,會增加的代價。

同理 $ costb[i][j] $ 表示已經填了 $ i $ 個白球,$ j $ 個黑球,該填第 $ j+1 $ 個黑球,會增加的代價。

則有轉移:
\[ dp[i][j] = min(dp[i-1][j]+costw[i-1][j],dp[i][j-1]+costb[i][j-1]) \]
邊界條件為 $ dp[0][0] = 0 $

dp的複雜度為 $ O(n^2) $

對於 $ cost $ 陣列來說,同樣可以 $ O(n^2) $ 預處理。

首先可以 $ O(n^2) $ 暴力處理出所有 $ cost[i][0] $ 和 $ costb[0][j] $

設 $ P_w[i] $ 表示寫著數字 $ i $ 的白球的初始位置,$ P_b[i] $ 表示寫著數字 $ i $ 的黑球的初始位置。

則對於 $ cost $ 陣列來說,有如下遞推:
\[ costw[i][j] = costw[i][j-1] + [P_b[j] > P_w[i+1]] \]

\[ costb[i][j] = costb[i-1][j] + [P_w[i] > P_b[j+1]] \]

所以預處理總複雜度也是 $ O(n^2) $ 的。

AC Code

#include <iostream>
#include <stdio.h>
#include <string.h>
#define MAX_N 2005

using namespace std;

int n;
int pw[MAX_N];
int pb[MAX_N];
int dp[MAX_N][MAX_N];
int costw[MAX_N][MAX_N];
int costb[MAX_N][MAX_N];

void read()
{
    scanf("%d",&n);
    char s[4]; int x;
    for(int i=1;i<=(n<<1);i++)
    {
        scanf("%s%d",s,&x);
        if(s[0]=='W') pw[x]=i;
        else pb[x]=i;
    }
}

void cal_c()
{
    for(int i=0;i<n;i++)
    {
        for(int j=1;j<=i;j++) costw[i][0]+=(pw[j]>pw[i+1]);
        for(int j=1;j<=n;j++) costw[i][j]=costw[i][j-1]+(pb[j]>pw[i+1]);
    }
    for(int j=0;j<n;j++)
    {
        for(int i=1;i<=j;i++) costb[0][j]+=(pb[i]>pb[j+1]);
        for(int i=1;i<=n;i++) costb[i][j]=costb[i-1][j]+(pw[i]>pb[j+1]);
    }
}

void cal_dp()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(i) dp[i][j]=min(dp[i][j],dp[i-1][j]+costw[i-1][j]);
            if(j) dp[i][j]=min(dp[i][j],dp[i][j-1]+costb[i][j-1]);
        }
    }
}

void work()
{
    cal_c();
    cal_dp();
    printf("%d\n",dp[n][n]);
}

int main()
{
    read();
    work();
}

相關文章