一、动态规划(3)递增序列

递增序列(INCNEASING SEQUENCES)
源程序名 INCSQ.??? (PAS, C, CPP)
可执行文件名 INCSQ.EXE
输入文件名 INCSQ. IN

输出文件名 INCSQ. OUT

给定一个数字串,请你插入若干个逗号,使得该数字串成为一个严格递增的数列且最后

一个数要尽可能小,在这个问题中,前导的零是允许出现在数的前面的。

输入

输入数据仅含一行,为一个长度不超过 80 的数字串。

输出

输出一个严格递增且最后一数最小的数列,相邻两个数之间用一个逗号隔开,如果有多

个数列满足要求,则输出第一个数最大的那个数列,若这样的解还不止一个,则输出第二个

数最大的那个数列,以此类推。

样例
INCSQ. IN
100000101
INCSQ. OUT

100,000101


分析:

设f[i,j]表示前i个字符划分成为j个部分,最后一个数字为最小值时最后数字的开头字母序号。比如100000101这个状态,f[9,1]=1,f[9,2]=4,就是说样例中的第四个数字‘0’是第二个数字的开头。
f[i,j]=max{k | 数字f[k-1,j-1]到k-1串的值小于k到i的串的值,即这种划分满足后面一个数大于这个数前面划分所得末尾最小值   且  f[k-1,j-1]是一种成立的划分}
然后就是处理边界,细节见程序。

上面说到了如何保证末尾最小,但是如何保证开头最大,我为此WA了两次。很简单,在末尾数相同的情况下,划分的数量也就是添加的逗号越少,相应开头数就越大。在所有f[len,j]的情况中,开头永远都是以末尾最小为前提的。
然后根据方程记录的结果倒推。

这道题灵活运用了动归的重要思想:无后效性。f[i,j]表示划分的断点位置,而不是划分出来的最后一个数的值,f[i,j]既表示了前i个数字划分成j个部分时,后面数字的最小值,又表示了这种状态。我们可以根据f[i,j]的值推知 f[f[i,j]-1,j-1]。这里的问题在于:第1个到f[i,j]-1,第f[i,j]个到i个,两个字串是独立的,前面的处理让第1个到f[i,j]-1的末尾数最小了,为后面提供成立的可能。

代码:

var
  a:string;
  f:array[0..80,0..80]of longint;
  len,i,j,k,last:longint;
  flag:boolean;

function compare(i,j,x,y:longint):boolean;
var
  k:longint;
begin
  while (a[i]=’0′) and (i<j) do inc(i);
  while (a[x]=’0′) and (x<y) do inc(x);
  if j-i<y-x then exit(true);
  if j-i>y-x then exit(false);
  for k:=0 to j-i do
  begin
    if a[i+k]<a[x+k] then exit(true);
    if a[i+k]>a[x+k] then exit(false);
  end;
  exit(false);
end;

procedure print(x,y:longint);
var
  i:longint;
begin
  if y=1 then
  begin
    for i:=1 to x do
    write(a[i]);
    if x<len then write(‘,’);
    exit;
  end;
  print(f[x,y]-1,y-1);
  for i:=f[x,y] to x do
  write(a[i]);
  if x<len then write(‘,’);
end;

begin
  assign(input,’incsq.in’);
  assign(output,’incsq.out’);
  reset(input);
  rewrite(output);

  readln(a);
  len:=length(a);
  last:=1;
  fillchar(f,sizeof(f),0);
  for i:=0 to len do f[i,1]:=1;
  for i:=2 to len do
    for j:=2 to i do
    begin
      flag:=false;
      for k:=j to i do
      if (f[k-1,j-1]<>-1) and compare(f[k-1,j-1],k-1,k,i) then
      begin
        f[i,j]:=k;
        flag:=true;
      end;
      if flag then last:=j else f[i,j]:=-1;
    end;
  for i:=len downto 1 do
  if f[len,last]<=f[len,i] then last:=i;
  print(len,last);
  writeln;

  close(input);
  close(output);
end.

    原文作者:动态规划
    原文地址: https://blog.csdn.net/boyxiejunboy/article/details/46876295
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞