C. Sakurako's Field Trip

https://codeforces.com/problemset/problem/2033/C

题意:给定n个数,如果i∈[1, n - 1]且a[i] == a[i + 1],则总的代价加1,现在可以任意交换a[i]和a[n - i + 1],求任意交换后的最小代价。

思路:动态规划,假想从两边向中间收缩,dp[i][0]和dp[i][1]分别表示走了i步时,不交换和交换的最小代价,可以得到转移方程:dp[i][0] = min(dp[i - 1][1] + ..., dp[i - 1][0] + ...), dp[i][1] = min(dp[i - 1][0] + ..., dp[i - 1][1] + ...)。最后,我们根据n的长度,来决定如何输出。假如n是奇数,那么直接输出最后一步的结果。假如n是偶数,那么在输出时,需要考虑最中间的两个数是否相等,因为不论如何交换,这两个数总是相邻的。

总结:这个题目有点像之前看过的一个传纸条的题目,印象里是从左上角往右下角传两个纸条,并且这两个纸条走的路径不能有重合,并且,每个格子里有个代价,问两个纸条到达右下角的最小还是最大代价。两个题目的相似点是,他们的状态有一些限制,并不能随意的走动,就是有一种互斥的感觉。 因为如果各自走各自的,那么最终将两个结果合并,我们不能保证这两个结果合在一起后,仍然是一个合法的路径,所以我们就在他们的每一步中,增加限制,保证他们不会相交。 根据这个思路,延申到当前的题目就是,我们限制了当前位置i不交换时,后面的位置一定是a[n - i + 1],而当前位置为a[n - i + 1]时,后面的位置一定是a[i],这样保证了状态的合法性,而为了保障这种状态的合法性,那么从步数作为状态去考虑,是一个理想的选择,而如果从前往后或者从后往前,一个维度单向走,好像都不太行。

代码不多,怎么感觉这题这么恶心呢。。

inline void solve() {
    int n;
    cin >> n;

    vector<int> a(n);
    for (auto& x : a) {
        cin >> x;
    }

    vector<array<int, 2>> dp(n, {INF, INF});
    dp[0][0] = dp[0][1] = 0;
    for (int i = 1; i < (n + 1) / 2; ++i) {
        dp[i][0] = min(dp[i - 1][0] + (a[i] == a[i - 1]) + (a[n - 1 - i] == a[n - i]), 
            dp[i - 1][1] + (a[i] == a[n - i]) + (a[n - 1 - i] == a[i - 1]));
        dp[i][1] = min(dp[i - 1][0] + (a[n - 1 - i] == a[i - 1]) + (a[i] == a[n - i]),
            dp[i - 1][1] + (a[n - 1 - i] == a[n - i]) + (a[i] == a[i - 1]));
    }
    if (n & 1) {
        cout << min(dp[n / 2][0], dp[n / 2][1]) << '\n';
    }
    else {
        int i = n / 2 - 1;
        cout << min(dp[i][0], dp[i][1]) + (a[i] == a[i + 1]) << '\n';
    }


}
posted @ 2025-07-03 09:55  _Yxc  阅读(7)  评论(0)    收藏  举报