HDU 6357.Hills And Valleys-字符串非严格递增子序列(LIS最长非下降子序列)+动态规划(区间翻转l,r找最长非递减子序列),好题哇 (2018 Multi-University Training Contest 5 1008)
6357.
自己感觉这是个好题,应该是经典题目,所以半路选手补了这道字符串的动态规划题目。
题意就是给你一个串,翻转任意区间一次,求最长的非下降子序列。
一看题面写的0≤Ai≤9 (i=1,2,⋯,n).就知道肯定有点东西,只要这么写,肯定就是有某个神奇的操作可以解决这道题目。
比赛的时候脑壳都要炸了也没想出来,补题的时候懂了,我可以定义一个b串为0123456789,这肯定是递增的,所以我翻转b的某个区间,然后去和a匹配,因为我把b再翻转回来,还是递增的。
当然了,因为是非严格的递增子序列,有相等的情况,所以相等的时候判断一下,直接+1就可以了。然后枚举一下b的翻转区间就可以了。因为还要输出翻转的区间,所以在dp的时候,用个数组分别保存一下l,r端点就可以了。其他的没什么。
感觉这种思路还是很值得思考的,不处理a,通过其他的操作间接处理a,以后写题要多想想,也写过类似思路的,通过其他的操作间接得到答案,但是一打比赛就没脑子,mdzz,为什么我这么菜༼༎ຶᴗ༎ຶ༽
参考了其他人的题解写出来的。
首先官方题解:
然后别人的博客:HDU6357——Hills And Valleys
代码:
1 //1008-6357-非严格递增子序列(最长非下降子序列)-字符串+dp-区间翻转,r找最长非递减子序列,好题哇 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<bitset> 7 #include<cassert> 8 #include<cctype> 9 #include<cmath> 10 #include<cstdlib> 11 #include<ctime> 12 #include<deque> 13 #include<iomanip> 14 #include<list> 15 #include<map> 16 #include<queue> 17 #include<set> 18 #include<stack> 19 #include<vector> 20 using namespace std; 21 typedef long long ll; 22 23 const double PI=acos(-1.0); 24 const double eps=1e-6; 25 const ll mod=1e9+7; 26 const int inf=0x3f3f3f3f; 27 const int maxn=1e5+10; 28 const int maxm=20+10; 29 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 30 31 int n,a[maxn],dp[maxn][maxm],b[maxm]; 32 int ans,L,R,l,r,h; 33 int al[maxn][maxm],ar[maxn][maxm]; 34 35 int solve() 36 { 37 for(int i=0;i<=h;i++) 38 dp[0][i]=0;//初始化 39 for(int i=1;i<=n;i++){//a和b匹配 40 for(int j=1;j<=h;j++){ 41 dp[i][j]=dp[i-1][j]; 42 al[i][j]=al[i-1][j];//记录左端点 43 ar[i][j]=ar[i-1][j];//记录右端点 44 if(a[i]==b[j]){//如果有相等的情况,+1 45 dp[i][j]=dp[i-1][j]+1; 46 if(l==j&&!al[i][j])//如果当前的j就是b开始翻转的左端点,更新记录 47 al[i][j]=i; 48 if(r==j)//右端点 49 ar[i][j]=i; 50 } 51 if(dp[i][j-1]>dp[i][j]){//更新答案 52 dp[i][j]=dp[i][j-1]; 53 al[i][j]=al[i][j-1]; 54 ar[i][j]=ar[i][j-1]; 55 } 56 } 57 } 58 return dp[n][h]; 59 } 60 61 char s[maxn]; 62 63 int main() 64 { 65 int t; 66 scanf("%d",&t); 67 while(t--){ 68 scanf("%d%s",&n,s+1); 69 for(int i=1;i<=n;i++) 70 a[i]=s[i]-'0'; 71 h=0; 72 for(int i=0;i<=9;i++) 73 b[++h]=i; 74 L=R=l=r=1; 75 ans=solve(); 76 for(int i=0;i<=9;i++){ 77 for(int j=i+1;j<=9;j++){ 78 h=0; 79 for(int k=0;k<=i;k++) 80 b[++h]=k;//翻转区间的左部分不变 81 l=h+1; 82 for(int k=j;k>=i;k--)//要翻转的区间把数字翻转 83 b[++h]=k; 84 r=h; 85 for(int k=j;k<=9;k++)//反转区间的右部分不变 86 b[++h]=k; 87 int tmp=solve(); 88 if(ans<tmp&&al[n][h]&&ar[n][h]){//更新结果 89 ans=tmp; 90 L=al[n][h]; 91 R=ar[n][h]; 92 } 93 } 94 } 95 printf("%d %d %d\n",ans,L,R); 96 } 97 }
溜了溜了,滚去补数据结构了。