LCS应用(2)—整理队形

      前面说了那么多,你可能都没有地方提交你打的代码。下面这道题是LCS的又一应用,原题见rqnoj P478。

      下面是题目描述。

 

 

【问题描述】(rqnoj478)

      学校艺术节上,规定合唱队要参加比赛,个个队员的衣服颜色不能很混乱:合唱队员应排成一横排,且衣服颜色必须是左右队称的。

      例如:“红蓝绿蓝红”或“红蓝绿绿蓝红”都是符合的,而“红蓝绿红”或“蓝绿蓝红”就不符合。

      合唱队人数自然很多,仅现有的同学就可能会有3000个。老师希望将合唱队调整的符合要求,但要尽量调整的少,减少麻烦。仅有一下3种情况算作是一次调整:

      1.在队伍中插入一个人;

      2.剔掉一个人;

      3.让一个人衣服换颜色。

      老师想知道目前的队形下,调整的符合要求需要最少多少次操作。

      因为合唱队很热门,所以你可以认为人数是无限的,即想加入一个人时,总有人加。同时衣服的颜色也是任意的。



【输入格式】

第一行是一个整数n(1<=n<=3000)

第二行是n个整数,从左到右表示现有的每个队员每个人的衣服颜色编号,都是不大于3000的自然数。



【输出格式】

一个数,即最少调整次数。



【样例输入】

5

1 2 2 4 3



【样例输出】

2

 

 

      这是一道区间类动态规划的题目。这道题与回文数很像,都是对已知序列进行操作,是它“对称”。但是这道题有三种操作方法,插入、删除和改变。插入操作和删除操作可以看做是等价的(即达到的效果相同),唯一与回文词不同的就是它多了改变这一操作。进过简单的思考可以发现,“改变”的实质是在已知序列的基础上将一对不匹配的字符转换成匹配的。我们定义f[i,j]表示要让队列从第i个到第j个左右对称需要的操作次数。那么,改变一个相当于f[i,j]:=f[i+1,j-1]+1。所以动态方程就可以推出:

LCS应用(2)—整理队形

      另外,当我们初始化的时候,只用把j>i的部分赋予极大值,因为只有这里存储的是区间最小值。

 

      参考代码:

  
    
1 program queue;
2 var
3 f: array [ 0 .. 3000 , 0 .. 3000 ] of longint;
4 a: array [ 1 .. 3000 ] of integer;
5 n,i,j,l:integer;
6 function min(x,y:longint):longint;
7 begin
8 if x < y then exit(x)
9 else exit(y);
10 end ;
11 begin
12 readln(n);
13 for i: = 1 to n do read(a[i]);
14 for i: = 1 to n do
15 for j: = i + 1 to n do
16 f[i,j]: = 100000000 ; // 初始化f数组
17 for l: = 1 to n - 1 do
18 for i: = 1 to n do
19 begin
20 j: = i + l;
21 if j > n then break;
22 if a[i] = a[j] then f[i,j]: = f[i + 1 ,j - 1 ] // a[i] = a[j],无需操作
23 else f[i,j]: = f[i + 1 ,j - 1 ] + 1 ; // 假设进行改变操作,并入下面的min也可
24 f[i,j]: = min(min(f[i,j],f[i + 1 ,j] + 1 ),f[i,j - 1 ] + 1 ); // 找到区间最优解
25 end ;
26 writeln(f[ 1 ,n]);
27 end .
 

 

 

 

      下面这道题和整理队形很相似,它提供的是另一种初始化的方法,另外题目加上了一些字串处理的内容,有兴趣的同学可以做一做。

 

 

【题目描述】

回文串是一个从前读和从后读一样的字符串。比如ABBA,MOM是回文串,但MATE不是。一个回文串可以通过修改某些位置变成一个回文串。如果一个字符串通过修改不超过k个位置变成一个回文串,那么这个字符串就被称为k回文串。一个最长并且是k回文串的子串被称为最长k回文子串。



【输入】

第一行一个字符串(长度不超过1000)和一个非负整数k(0<=k<=字符串长度)。字符串只包含’a’到‘z’。



【输出】输出最长k回文子串的长度。



【输入输出样例1】

Palindrome.in	 Palindrome.out

abba 0	         4



【输入输出样例2】

Palindrome.in	 Palindrome.out

mate 1	         3



【输入输出样例3】

Palindrome.in    Palindrome.out

zabcddcbxy 1	  8

 

      这道题与整理队形一样,唯一的不同就是对于这个给定的串只能进行“改变”操作,不能进行“删除”或者“添加”。这样题目就更加简单了。

      另外提一下参考代码中的初始化。它先把制作长度为2的回文子串所需要的步数通过一个简单的枚举计算出来,就免的赋极大值再从头去找。因为长度为2的情况已经找过了,只要从长度为3的情况开始更新f数组即可。

      我在处理字符串的时候是一个字符一个字符读入的,操作的相对麻烦,参考代码中我改成了相对方便的ansistring。

 

      参考代码:

 

  
    
1 program palindrome;
2 var
3 f: array [ 0 .. 1001 , 0 .. 1001 ] of longint;
4 a: array [ 1 .. 1000 ] of char;
5 i,j,n,k,po,t,l,max:longint;
6 s:ansistring;
7 temp:string;
8 c:char;
9 begin
10 readln(s);
11 i: = pos( ' ' ,s); // 处理读入的字串
12 temp: = copy(s,i + 1 ,length(s) - i);
13 delete(s,i,length(s) - i + 1 );
14 val(temp,k,i);
15 n: = length(s);
16 for i: = 1 to n - 1 do // 枚举长度为2的情况
17 if s[i] <> s[i + 1 ] then f[i,i + 1 ]: = 1 ;
18 for l: = 3 to n do // 从3开始更新(p.s.注意这里不是n - 1
19 for i: = 1 to n - l + 1 do
20 begin
21 j: = i + l - 1 ;
22 if s[i] = s[j] then f[i,j]: = f[i + 1 ,j - 1 ]
23 else f[i,j]: = f[i + 1 ,j - 1 ] + 1 ;
24 end ;
25 max: = 0 ;
26 for i: = 1 to n do
27 for j: = i to n do
28 if (f[i,j] <= k) and (max < j - i + 1 ) then max: = j - i + 1 ; // 寻找达到条件的最大值
29 writeln(max);
30 end .

 

 

(saltless原创,转载请注明出处)

 

 

你可能感兴趣的:(应用)