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。所以动态方程就可以推出:

      另外,当我们初始化的时候,只用把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原创,转载请注明出处)

 

 

posted on 2010-08-10 08:40  saltless  阅读(786)  评论(7编辑  收藏  举报

导航