当前位置: 首页 > 工具软件 > Rescue! Max > 使用案例 >

Codeforces 1562E Rescue Niwen!

段成益
2023-12-01

Codeforces 1562E Rescue Niwen!

思路

对于字符串 s 1 s 2 s 3 . . . s n s_1s_2s_3...s_n s1s2s3...sn

我们先来考虑一个子集,仅讨论以 s 1 , s 2 s_1,s_2 s1,s2 两个位置为开头的扩展字符串列中的最长递增子序列,即 s 1 ; . . . ; s 1 , . . . , s n ; s 2 ; . . . ; s 2 . . . s n s_1;...;s_1,...,s_n;s_2;...;s_2...s_n s1;...;s1,...,sn;s2;...;s2...sn 中的最长递增子序列

情况一: s 1 > s 2 s_1>s_2 s1>s2

显然,此时 s 1 , s 2 s_1,s_2 s1,s2 只能二则择一,即,要么选 s 1 s_1 s1 为开头的扩展字符串集合作为答案,要么选 s 2 s_2 s2

情况二: s 1 < s 2 s_1<s_2 s1<s2

显然,此时应该贪心的都选

情况三: s 1 = s 2 s_1=s_2 s1=s2

那么我们继续来考虑各自的下一位,即 s 1 + 1 = s 2 s_{1+1}=s_2 s1+1=s2 s 1 + 2 = s 3 s_{1+2}=s_3 s1+2=s3 的关系

如果 s 2 = s 3 s_2=s_3 s2=s3 ,继续考虑各自的下一位,即 s 3 s_3 s3 s 4 s_4 s4 的关系

如果 s 2 < s 3 s_2<s_3 s2<s3 ,那么类似情况二,可以贪心选完 s 1 s_1 s1 开头的,再从 s 2 s_2 s2 开头且到了 s 3 s_3 s3 的开始选到最后

举个例子,假设串是 a a a b b c aaabbc aaabbc ,那么只考虑前两个位置为开头扩展出来应该是 1   a 1\ a 1 a , 2   a a 2\ aa 2 aa , 3   a a a 3\ aaa 3 aaa , 4   a a a b 4\ aaab 4 aaab , 5   a a a b b 5\ aaabb 5 aaabb , 6   a a a b b c 6\ aaabbc 6 aaabbc , 7   a 7\ a 7 a , 8   a a 8\ aa 8 aa , 9   a a b 9\ aab 9 aab , 10   a a b b 10\ aabb 10 aabb , 11   a a b b c 11\ aabbc 11 aabbc

那么此时 s 1 = s 2 s_1=s_2 s1=s2 s 2 = s 3 s_2=s_3 s2=s3 ,然后 s 3 < s 4 s_3<s_4 s3<s4 ,那么首先贪心选 s 1 s_1 s1 开头的,于是选了 1 2 3 4 5 6 ,然后从 s 2 s_2 s2 开头且到 s 4 s_4 s4 的开始选,于是选了 9 10 11

怎么说明是最优呢,显然对于相等的字符串,是肯定要贪心选择位置更小的,而对于前面相等的位置,选 1 肯定比选 7 优,选 2 肯定比 选 8 优,

所以前面相等的部分不用再考虑,对于不相等的部分这种贪心策略能达到可选的最大值

如果 s 2 > s 3 s_2 > s_3 s2>s3 ,首先,对于前面相等的部分是不用考虑的,亦如上述

对于不相等的部分,容易发现选了一边就无法再选另一边了

综上,设 d p i dp_i dpi 表示上一次选了 s 的第 i 位作为开头的扩展串

预处理一下以 i,j 开头的串冲突在哪一位

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;

const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int t, n;
char s[5050];
ll dp[5050];
int pre[5050][5050];

void debug() {
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++)
            cout << pre[i][j] << ' ';
        cout << endl;
    }
}

int main() {
    cin >> t;
    while(t--) {
        cin >> n;
        cin >> s;
        ll ans = 0;
        for(int i=1; i<n; i++) {
            int r = 1;
            for(int j=1; j+i<=n; j++) {
                if(s[j-1] != s[i+j-1]) {
                    while(r <= j) {
                        pre[r][r+i] = j;
                        ++r;
                    }
                }
            }
        }
        // debug();
        for(int i=1; i<=n; i++) {
            dp[i] = 0;
            for(int j=i-1; j >= 0; j--) {
                if(j == 0 || s[i-1] > s[j-1]) dp[i] = max(dp[i], dp[j]+n-i+1);
                else if(s[i-1] == s[j-1]) {
                    int r = pre[j][i];
                    if(r && s[r-1] < s[r-1+i-j]) dp[i] = max(dp[i], dp[j]+n-(r+i-j)+1);
                }
            }
            ans = max(ans, dp[i]);
            // cout << i << ' ' << dp[i] << endl;
        }
        cout << ans << endl;
    }
    return 0;
}
 类似资料:

相关阅读

相关文章

相关问答