• [USACO17JAN]Subsequence Reversal


    题目:Subsequence Reversal

    网址:https://www.luogu.com.cn/problem/P3607

    题目描述

    Farmer John is arranging his (N) cows in a line to take a photo ((1 leq N leq 50)). The height of the (i)th cow in sequence is (a(i)), and Farmer John thinks it would make for an aesthetically pleasing photo if the cow lineup has a large increasing subsequence of cows by height.

    To recall, a subsequence is a subset (a(i_1), a(i_2), ldots, a(i_k)) of elements from the cow sequence, found at some series of indices (i_1 < i_2 < ldots < i_k). We say the subsequence is increasing if (a(i_1) leq a(i_2) leq ldots leq a(i_k)).

    FJ would like there to be a long increasing subsequence within his ordering of the cows. In order to ensure this, he allows himself initially to choose any subsequence and reverse its elements.

    For example, if we had the list
    
    1 6 2 3 4 3 5 3 4
    We can reverse the chosen elements
    
    1 6 2 3 4 3 5 3 4
      ^         ^ ^ ^
    to get
    
    1 4 2 3 4 3 3 5 6
      ^         ^ ^ ^
    

    Observe how the subsequence being reversed ends up using the same indices as it initially occupied, leaving the other elements unchanged.

    Please find the maximum possible length of an increasing subsequence, given that you can choose to reverse an arbitrary subsequence once.

    Input

    The first line of input contains (N). The remaining (N) lines contain (a(1) ldots a(N)), each an integer in the range (1 ldots 50).

    Output

    Output the number of elements that can possibly form a longest increasing subsequence after reversing the contents of at most one subsequence.

    输入输出样例

    输入 #1

    9
    1
    2
    3
    9
    5
    6
    8
    7
    4
    

    输出 #1

    9
    

    这道题目的解法非常精彩。

    我简要分析。

    1. 最长上升子序列;
      方程显然成立:(dp[i] = max(dp[j] + 1))
    2. 求解任意区间内的最长上升子序列;
      (dp[i,j])为区间([i,j])中最长上升子序列长度;
      但状态刻画不清晰,应改为选(a_i)为第(1)个数的最长上升子序列;
      对此状态,有:(dp[i,j]=max(dp[k,j]+1))
    3. 对于一个序列,允许挑选一个子序列进行翻转,求最长上升子序列。
      我们先不考虑怎么做,反之,解决翻转的问题。

    考虑实际操作的时候,翻转相当于元素交换,换句话说,就是挑偶数个数,从里向外(反过去一样)交换元素。
    我们将一次性的操作转化为有约束条件的连续操作了。

    考虑状态定义。不妨设 (dp[i,j,d,u]) 代表 ([i,j]) 中最小值不小于 (d),最大值不大于 (u) 的LIS。
    先写转移。
    (dp[i,j,d,u]=max(max(dp[i+1,j,d,u]+(a[i]==d),dp[i,j-1,d,u]+(a[j]==u),dp[i+1,j-1,d,u]+(a[i]==u)+(a[j]==d)),max(dp[i,j,d+1,u],dp[i,j,d,u-1])))
    首先,([i,j])(a_i)(a_j) 两个数不交换,交换的话特判一下即可。约束条件变窄可以。

    C ++ AC代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int SIZE = 50 + 5;
    int n, a[SIZE] = {}, dp[SIZE][SIZE][SIZE][SIZE] = {};
    int main()
    {
    	scanf("%d", &n);
    
    	for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
    	for(int i = 1; i <= n; ++ i)
    	{
    		for(int d = 1; d <= a[i]; ++ d)
    		{
    			for(int u = a[i]; u <= 50; ++ u)
    			{
    				dp[i][i][d][u] = 1;
    			}
    		}
    	}
    	for(int len1 = 2; len1 <= n; ++ len1)
    	{
    		for(int i = 1, j = len1; j <= 50; ++ i, ++ j)
    		{
    			for(int len2 = 2; len2 <= 50; ++ len2)
    			{
    				for(int d = 1, u = len2; u <= 50; ++ d, ++ u)
    				{
    					int &ans = dp[i][j][d][u];
    					ans = max(dp[i + 1][j][d][u] + (a[i] == d), dp[i][j - 1][d][u] + (a[j] == u));
    					ans = max(ans, dp[i + 1][j - 1][d][u] + (a[j] == d) + (a[i] == u));
    					ans = max(ans, dp[i][j][d + 1][u]);
    					ans = max(ans, dp[i][j][d][u - 1]);
    				}
    			}
    		}
    	}
    	printf("%d
    ", dp[1][n][1][50]);
    	return 0;
    }
    

    总结回顾

    这道题的整个做法实在精彩!题目的转化值得回味。

    参考文献

  • 相关阅读:
    SpringIOC的小例子
    java中递归实现复制多级文件夹
    快速排序和几种简单排序
    Oracle面试的基本题
    多态的两个小例子
    单例模式
    内部类与匿名内部类
    C#
    C#
    C#
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13548681.html
Copyright © 2020-2023  润新知