CW 11.02 模拟赛 FSYo T2

题面

自出题
挂个 pdf
题面下载

算法

看到交换, 这里有一个套路:
确定最终的形态后, 交换次数即为逆序对个数
我们直接设 \(f_{i, j, k, 0 / 1 / 2}\) 表示 \(3\) 种颜色填到哪里了,最后一个是什么颜色,逆序对数最少是多少

转移分最后一个是什么颜色讨论

关于 \(O(1)\) 求逆序对的方法:

    if(i==0 && a) f[a][b][c][0] = min(f[a][b][c][0], min(f[a-1][b][c][1], f[a-1][b][c][2])+max(0, b-pre[0][a].first)+max(0, c-pre[0][a].second));
    if(i==1 && b) f[a][b][c][1] = min(f[a][b][c][1], min(f[a][b-1][c][0], f[a][b-1][c][2])+max(0, a-pre[1][b].first)+max(0, c-pre[1][b].second));
    if(i==2 && c) f[a][b][c][2] = min(f[a][b][c][2], min(f[a][b][c-1][0], f[a][b][c-1][1])+max(0, a-pre[2][c].first)+max(0, b-pre[2][c].second));

\(O(n ^ 3)\)

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 202;
const int N = 505;
const int INF = 1e9;

int n, f[maxn][maxn][maxn][3], pos[3];
pair<int, int> pre[3][maxn];
char s[402];

inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    freopen("s.in", "r", stdin);
    freopen("s.out", "w", stdout);

    n = read();
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i++)
    {
        if (s[i] == 'R')
            pos[0]++, pre[0][pos[0]] = make_pair(pos[1], pos[2]);
        else if (s[i] == 'G')
            pos[1]++, pre[1][pos[1]] = make_pair(pos[0], pos[2]);
        else
            pos[2]++, pre[2][pos[2]] = make_pair(pos[0], pos[1]);
    }
    memset(f, 0x3f, sizeof(f));
    if (pos[0])
        f[1][0][0][0] = 0;
    if (pos[1])
        f[0][1][0][1] = 0;
    if (pos[2])
        f[0][0][1][2] = 0;
    for (int a = 0; a <= pos[0]; a++)
    {
        for (int b = 0; b <= pos[1]; b++)
        {
            for (int c = 0; c <= pos[2]; c++)
            {
                for (int i = 0; i <= 2; i++)
                {
                    if (i == 0 && a)
                        f[a][b][c][0] = min(f[a][b][c][0], min(f[a - 1][b][c][1], f[a - 1][b][c][2]) + max(0, b - pre[0][a].first) + max(0, c - pre[0][a].second));
                    if (i == 1 && b)
                        f[a][b][c][1] = min(f[a][b][c][1], min(f[a][b - 1][c][0], f[a][b - 1][c][2]) + max(0, a - pre[1][b].first) + max(0, c - pre[1][b].second));
                    if (i == 2 && c)
                        f[a][b][c][2] = min(f[a][b][c][2], min(f[a][b][c - 1][0], f[a][b][c - 1][1]) + max(0, a - pre[2][c].first) + max(0, b - pre[2][c].second));
                }
            }
        }
    }
    printf("%d\n", min(f[pos[0]][pos[1]][pos[2]][0], min(f[pos[0]][pos[1]][pos[2]][1], f[pos[0]][pos[1]][pos[2]][2])));

    return 0;
}

总结

算套路吧(?)

逆序对求法值得学习, 类似于前缀和

posted @ 2024-11-02 16:20  Yorg  阅读(4)  评论(0编辑  收藏  举报