HDU-6357(...这道题还真是巧妙...)

                                                                                Hills And Valleys
                                                           Time Limit: 2000/1000 MS (Java/Others)

Problem Description
Tauren has an integer sequence A of length n (1-based). He wants you to invert an interval [l,r] (1≤l≤r≤n) of A (i.e. replace Al,Al+1,⋯,Ar with Ar,Ar−1,⋯,Al) to maximize the length of the longest non-decreasing subsequence of A. Find that maximal length and any inverting way to accomplish that mission.
A non-decreasing subsequence of A with length m could be represented as Ax1,Ax2,⋯,Axm with 1≤x1<x2<⋯<xm≤n and Ax1≤Ax2≤⋯≤Axm.

Input
The first line contains one integer T, indicating the number of test cases.
The following lines describe all the test cases. For each test case:
The first line contains one integer n.
The second line contains n integers A1,A2,⋯,An without any space.
1≤T≤100, 1≤n≤105, 0≤Ai≤9 (i=1,2,⋯,n).
It is guaranteed that the sum of n in all test cases does not exceed 2⋅105.

Output
For each test case, print three space-separated integers m,l and r in one line, where m indicates the maximal length and [l,r] indicates the relevant interval to invert.

Sample Input
2
9
864852302
9
203258468
 

Sample Output
5 1 8
6 1 2
 

题意:给你一个长度为n,只包含0~9的序列,可翻转一个区间,求非严格最长上升子序列的长度.

思路:官方题解的思路甚是巧妙...因为只有0~9个数字,所以我们可以求非严格最长上升子序列转化为求可重复的最长公共子序列问题,其实通常的求最长上升子序列的问题我们也可以转化来做,虽然复杂度高点,但不失为是一种方法.所以我们按照dp思想写写转移方程就能求最长公共子序列了.

每次我们可以把b数组中0~9的区间反转一部分,这个操作之后求最长公共子序列就相当于在a数组中翻转了左端点为x,右端点为y的区间.还有就是我们需要保留一份端点值,因为是可重复的子序列嘛,所以如果不保留一份端点值的话就不能留下a中在边界处的值,这个可以自己举例子试试~比如123354356,我们想在a数组中翻转5,4,3,就相当于在b中翻转3,4,5,如果不保留一份3,5在两边的话,求出来的有效序列只能是123456,而正确的应该是123334556.

还有怎么求在a数组中翻转的左右端点,我们直接让他跟着最大值走就好了.

代码:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 2e5+5;
const double eps = 1e-12;
const int inf = 0x3f3f3f3f;
map<int,int>::iterator it;

int n;
int dp[maxn][13];
int al[maxn][13],ar[maxn][13],bl,br;
int minv,maxv,b[15];
char a[maxn]; 

void init()
{
	minv = 9;
	maxv = 0;
}

void sswap(int l,int r)
{
	int cnt = 0;
	for(int i = 0;i<= l;i++)
		b[cnt++] = i;
	for(int i = r;i>= l;i--)
		b[cnt++] = i;
	for(int i = r;i< 10;i++)
		b[cnt++] = i;
	return ;
}

int solve()
{
	for(int i = 0;i<= 12;i++) dp[0][i] = 0;
	for(int i = 1;i<= n;i++)
	{
		for(int j = 0;j< 12;j++)
		{
			dp[i][j] = dp[i-1][j];
			ar[i][j] = ar[i-1][j];//存右端点
			al[i][j] = al[i-1][j];//存左端点
			
			if(a[i] == b[j])
			{
				dp[i][j]++;
				if(bl == j&&al[i][j] == 0)
                    al[i][j] = i;
                if(br == j)
                    ar[i][j] = i;
			}
			if(j> 0&&dp[i][j-1]> dp[i][j])
			{
				dp[i][j] = dp[i][j-1];
				ar[i][j] = ar[i][j-1];
				al[i][j] = al[i][j-1];
			}
		}
	}
	return dp[n][11];
}

int main()
{
	int t;
	cin>>t;
	
	while(t--)
	{
		init();
		scanf("%d",&n);
		scanf(" %s",a+1);
		for(int i = 0;i< 12;i++) b[i] = i;
		for(int i = 1;i<= n;i++)
		{
			 a[i]-= '0';
			 minv = min(minv,(int)a[i]);
			 maxv = max(maxv,(int)a[i]);
		}
		
		int ans = solve(),ansl = 1,ansr = 1;
		for(int i = minv;i<= maxv;i++)
		{
			for(int j = i+1;j<= maxv;j++)
			{
				sswap(i,j);
				bl = i+1,br = j+1;
				int tmp = solve();
				if(tmp>= ans)
				{
					ans = tmp;
					if(al[n][11]&&ar[n][11])
						ansl = al[n][11],ansr = ar[n][11];
				}
			}
		}
		
		printf("%d %d %d\n",ans,ansl,ansr);
	}
	
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值