DP训练

bzoj1996 合唱队

题意:给出一个序列,从左往右将元素加入新序列,如果这个数小于之前的数就插入到队列的左边,如果大于就插入到右边。现给定最后的队列,求一开始满足的序列数

分析:f[i,j,1]表示对于i..j区间最后一个加入的元素是最左边的总方案数 f[i,j,2]表示对于i..j区间最后一个加入的元素是最右边的总方案数,区间i..j用i+1..j和i..j-1区间来更新,要判断端点是否符合,初值为f[i,i,1]:=1 f[i,i,2]:=0; 只能给其中一个赋值。

program ssad;
var
  f:array[0..1000,0..1000,1..2]of longint;
  a:array[0..1000]of longint;
  n,i,m,j:longint;
begin
  readln(n); m:=19650827;
  for i:=1 to n do
   read(a[i]);
  for i:=n downto 1 do
   for j:=i to n do
    if i=j then begin f[i,j,1]:=1; f[i,j,2]:=0; end
    else
     begin
       f[i,j,1]:=(f[i+1,j,1]*ord(a[i]<a[i+1])+f[i+1,j,2]*ord(a[i]<a[j])) mod m;
       f[i,j,2]:=(f[i,j-1,1]*ord(a[j]>a[i])+f[i,j-1,2]*ord(a[j]>a[j-1])) mod m;
     end;
  writeln((f[1,n,1]+f[1,n,2]) mod m);
end.
View Code

 

bzoj1786

题意:给出n个数,其中有的数字用-1表示意思为不知道,每个数都在[1..k]区间,求往-1处填[1..k]数字使这组数的逆序对数最小。

分析:最优方案中-1中填入的数从左往右必然是单调不减的,也就是-1中的数之间不存在逆序对(可以证明),这样就简单了,f[i,j]表示第i个-1中填入的数<=j的最小逆序对数,w[i,j]表示i位置左边比j大的数个数(包括i位置本身,v也一样),v[i,j]表示i位置右边比它小数的个数,w,v预处理出来,然后f[i,j]:=min(f[i,j-1],f[i-1,j]+w[q[i],j]+v[q[i],j]),q[i]为第i个-1在数列中位置。

program pair;
var
  f,w,v:array[0..10001,0..100]of longint;
  a,q:array[0..10000]of longint;
  n,i,m,k,j,ans:longint;
function min(x,y:longint):longint;
begin
  if x<y then min:=x else min:=y;
end;
begin
  readln(n,k);
  for i:=1 to n do read(a[i]);
  for j:=1 to k do
   for i:=1 to n do
    begin
    w[i,j]:=w[i-1,j];
    if (a[i]>=0) then if a[i]>j then w[i,j]:=w[i,j]+1;
    end;
  for j:=1 to k do
   for i:=n downto 1 do
    begin
    v[i,j]:=v[i+1,j];
    if (a[i]>=0) then if a[i]<j then v[i,j]:=v[i,j]+1;
    end;
  for i:=1 to n do if a[i]<0 then begin inc(m);q[m]:=i; end;
  for i:=1 to m do
    for j:=0 to k do f[i,j]:=maxlongint div 3;
  for i:=0 to k do f[0,i]:=0;
  for i:=1 to m do
   for j:=1 to k do
     f[i,j]:=min(f[i,j-1],f[i-1,j]+w[q[i],j]+v[q[i],j]);
  for i:=1 to n do
   if a[i]>=0 then ans:=ans+w[i,a[i]];  inc(ans,f[m,k]);
  writeln(ans);
end.
View Code

 

posted @ 2016-08-18 21:39  QTY_YTQ  阅读(210)  评论(0)    收藏  举报