#804 div.2 D. Almost Triple Deletions 【dp】

题目地址

 想到了这一步(就是各种 dp 瞎搞):

for(int i=1;i<=n;i++)dp[0][i] = 0;
        for(int i=0;i<n;i++){
            for(int j=1;j<=n;j++){
                if( dp[i][j] < 0 )continue;
                if( i+1<=n && a[i+1] == j )dp[i+1][j] = max( dp[i+1][j], dp[i][j]+1 );
                // if( i+3<=n && a[i+3] == j )dp[i+3][j] = max( dp[i+3][j], dp[i][j]+1 );
                if( i+2<=n && a[i+1] != a[i+2] )dp[i+2][j] = max( dp[i+2][j], dp[i][j] );
            }
        }

 感觉是个 dp, 当时想着要么不执行删除操作, d p [ i ] + 1 → d p [ i + 1 ] dp[i]+1 \rightarrow dp[i+1] dp[i]+1dp[i+1],执行操作的话要求就是相邻两个数不相同 balabala…
 然后样例过不了,一是从长度为 0 的状态扩展到长度为 1 的状态 (个人喜欢称作从无到有的 active),但这要考虑区间 [    1 , i    ] [\; 1, i\; ] [1,i] 是否能全部删掉;二是 d p [ i ] → d p [ j ] dp[i]\rightarrow dp[j] dp[i]dp[j] 的话,中间可能不止删两个,可能删一段,而每删删两个的话相邻的数就变了,不好判断。

 然后又是经典的 “最大数量不超过总数一半” 系列,满足这个条件且长度为偶数的区间可以全部删掉,用一个数组 d e l [ i ] [ j ] del[i][j] del[i][j] 表示区间 [ i , j ) [i,j) [i,j) (左闭右开,这样可以表示长度为 0 的空区间) 可以通过若单次操作全部删掉,接下来就是个经典的 dp 了,不过最后答案获取有点特别,CF官方题解是用一个状态 d p [ n + 1 ] dp[n+1] dp[n+1] ;我就直接 遍历 + 判断更新

int a[5010],dp[5050];
bool del[5050][5050]; // del[i][j] -> [ i, j )
int cnt[5050];

int main() {
    int T = 1;
    scanf("%d",&T);
    while(T--){
        int n,m;
        int k;
        scanf("%d",&n);

        for(int i=1;i<=n;i++)scanf("%d",a+i);

        for(int i=1;i<=n+1;i++){ // 这里 i 要跑到 n+1
            for(int i=1;i<=n;i++)cnt[i] = 0;
            int Max = 0;
            for(int j=i;j<=n+1;j++){
                del[i][j] = false;
                if( (j-i)%2 == 0 && Max <= (j-i)/2 ){
                    del[i][j] = true;
                }
                cnt[ a[j] ]++;
                if( Max < cnt[ a[j] ] ) Max = cnt[ a[j] ];
            }
        }

        dp[0] = 0;
        for(int i=1;i<=n;i++){
            // dp[i] = 1;
            dp[i] = -1;
            for(int j=0;j<i;j++){
                if( ( j==0 || a[i] == a[j] ) && dp[j] >= 0 && del[j+1][i] ){
                    dp[i] = max( dp[i], dp[j]+1 );
                }
            }
        }

        // int ans = *max_element(dp+1,dp+n+1);
        int ans = 0;
        for(int i=1;i<=n;i++){
            if( del[i+1][n+1] )ans = max( ans, dp[i] );
        }

        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值