网易2017秋招编程题——回文序列 解题报告
Problem:https://www.nowcoder.com/question/next?pid=2811407&qid=46573&tid=6015849
如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列。例如:
{1, 2, 1}, {15, 78, 78, 15} , {112} 是回文序列,
{1, 2, 2}, {15, 78, 87, 51} ,{112, 2, 11} 不是回文序列。
现在给出一个数字序列,允许使用一种转换操作:
选择任意两个相邻的数,然后从序列移除这两个数,并用这两个数字的和插入到这两个数之前的位置(只插入一个和)。
现在对于所给序列要求出最少需要多少次操作可以将其变成回文序列。
输入描述:
输入为两行,第一行为序列长度n ( 1 ≤ n ≤ 50) 第二行为序列中的n个整数item[i] (1 ≤ iteam[i] ≤ 1000),以空格分隔。
输出描述:
输出一个数,表示最少需要的转换次数
输入例子:
4 1 1 1 3
输出例子:
2
Solution1:
a[i]~a[i+j]变成回文序列,有三种方法:
1.a[i]~a[i+j]合并成一个数
2.a[i]~a[k],a[k+1]~a[i+j]分别合并成一个数,且两者数值相等
3.a[i]~a[k]合并成一个数,a[l]~a[i+j]合并成一个数,a[k+1]~a[l-1]已构成回文序列,该回文序列长度为f[k+1][l-1]
1 #include <iostream> 2 #define maxn 50 3 #define maxs 1000 4 using namespace std; 5 6 int main() 7 { 8 long n,i,j,k,l,a[maxn+1],f[maxn+1][maxn+1],ans[maxn+1][maxn+1]; 9 cin>>n; 10 for (i=1;i<=n;i++) 11 { 12 cin>>a[i]; 13 ans[i][i]=a[i]; 14 } 15 //the total of a[i]~a[j] 16 for (i=1;i<n;i++) 17 for (j=i+1;j<=n;j++) 18 ans[i][j]=ans[i][j-1]+a[j]; 19 //length:j+1 20 for (j=0;j<=n-1;j++) 21 //begining position:i 22 for (i=1;i<=n-j;i++) 23 { 24 //a[i]~a[i+j]合并成一个数 25 f[i][i+j]=j; 26 //a[i]~a[k]合并成一个数,a[l]~a[i+j]合并成一个数,a[k+1]~a[l-1]已构成回文序列,该回文序列长度为f[k+1][l-1] 27 for (k=i;k<=i+j-2;k++) 28 for (l=k+2;l<=i+j;l++) 29 if (ans[i][k]==ans[l][i+j]) 30 //(k-i)+((i+j)-l)=k+j-l 31 f[i][i+j]=min(f[i][i+j],f[k+1][l-1]+k+j-l); 32 //a[i]~a[k],a[k+1]~a[i+j]分别合并成一个数,且两者数值相等 33 for (k=i;k<=i+j;k++) 34 if (ans[i][k]==ans[k+1][i+j]) 35 f[i][i+j]=min(f[i][i+j],j-1); 36 } 37 cout<<f[1][n]<<endl; 38 return 0; 39 }
Solution2:
贪心
长度为n的数列(a[1],a[2],……,a[n]) [1 ≤ a[i] ≤ 1000] 经过变换构成回文序列(b[1],b[2],……,b[m]):
设包含a数列元素个数最少的b[1]为:b[1]=a[1]+a[2]+……+a[p],对应的b[m]=a[q]+a[q+1]+……+a[n]
因为a[i]>0,所以q不同,则b[m]不同,所以b[m]具有唯一性。
当存在p',使得p'>p,且b[1]'=a[1]+a[2]+……+a[p'],b[m]'=a[q']+a[q'+1]+……+a[n], b[1]'=b[m]',
有b[m]'=b[1]'>b[1]=b[m],即q'<q,且a[p+1]+……+a[p']=a[q']+……+a[q-1]。
当某回文序列b[1]'=a[1]+a[2]+……+a[p'],b[m]'=a[q']+a[q'+1]+……+a[n],
可以修改b[1]'为:b[1]=a[1]+a[2]+……+a[p],修改b[m]'为:b[m]=a[q]+a[q+1]+……+a[n]
修改b[2]'为:b[2]=b[2]'+a[p+1]+……+a[p'],修改b[m-1]为:b[m-1]'=b[m-1]+a[q']+……+a[q-1]。
修改后仍为回文序列,且操作次数与修改前相同。
同理可以把b[2]'修改为b[2](包含a数列元素个数最少),把b[3]'修改为b[3](包含a数列元素个数最少),依次类推,直到修改后剩下的数能构成回文序列,且被修改的部分(左/右)构成回文序列的部分(左/右),所以能构成回文序列,节省的操作次数为原来剩下的数还要继续操作变成回文序列的次数。
采用以下方法构成回文序列:
选取包含a数列元素个数最少的b[1]和对应的b[m],在此基础上选取包含a数列元素个数最少的b[2]和对应的b[m-1],依次类推,直到a所有的元素被选取完毕。
上述已证明任意方法能转变成此方法,此方法必不会增加操作次数(操作次数与修改前相同),且有可能节省操作次数,所以能证明得到用此方法构成回文序列操作次数最小。
#include <iostream> #define maxn 50 using namespace std; int main() { long n,a[maxn+1],i,l,r,ans=0; cin>>n; for (i=1;i<=n;i++) cin>>a[i]; l=1; r=n; while (l<r) { if (a[l]==a[r]) { l++; r--; } else if (a[l]<a[r]) { l++; a[l]+=a[l-1]; ans++; } else { r--; a[r]+=a[r+1]; ans++; } } cout<<ans<<endl; return 0; }