[BZOJ1786&&BZOJ1831]配对 dp

来源:互联网 发布:海意it 编辑:程序博客网 时间:2024/05/30 02:53

首先我们可以发现所有-1的地方单调不降的填是会比正着填更优的。证明的话应该是反正吧。考虑???A????B??,(?全是确定的数),首先A和B的顺序在A左边和B右边并不会增加逆序对数,中间的数如果< A或 >B都会产生1的贡献,倘若A>B,< A和 >B的贡献个数是大于等于> A和< B的贡献个数的,再加上A,B本身产生的一个逆序对,还不如反过来填成A< B。
所以接下来就是DP了,f[i,j]表示第i个-1的位置,填j的最少逆序对增量,那么f[i,j]=min{f[i-1,k]}(1<=k<=j)+large[place[i],j]+little[place[i],j]。place表示-1的位置,large[i,j]表示第i个数是j的时候前面有多少个大于它的,little则表示后面小于的。large,little可以预处理,min可以在dp过程中更新。
复杂度:O(nk)。
代码:

var  n,k,i,j,num,ans:longint;  a,pl,c:array[0..10010]of longint;  f,lar,lit,min:array[0..10010,0..110]of longint;const maxl=1000000000;function qmin(x,y:longint):longint;begin  if x>y then exit(y)  else exit(x);end;begin  readln(n,k);  num:=0;  for i:=1 to n do  begin    read(a[i]);    if a[i]=-1 then    begin      inc(num);      pl[num]:=i;    end;  end;  for i:=2 to n do    for j:=1 to k do    begin      lar[i,j]:=lar[i-1,j];      if (a[i-1]>j)and(a[i-1]<>-1) then inc(lar[i,j]);    end;  for i:=n-1 downto 1 do    for j:=1 to k do    begin      lit[i,j]:=lit[i+1,j];      if (a[i+1]<j)and(a[i+1]<>-1) then inc(lit[i,j]);    end;  for i:=1 to num do    min[i,0]:=maxl;  for i:=1 to num do    for j:=1 to k do    begin      f[i,j]:=min[i-1,j]+lit[pl[i],j]+lar[pl[i],j];      min[i,j]:=qmin(f[i,j],min[i,j-1]);    end;  ans:=maxl;  for i:=1 to k do    ans:=qmin(ans,f[num,i]);  for i:=1 to n do    if a[i]<>-1 then ans:=ans+lar[i,a[i]];  writeln(ans);end.
0 0
原创粉丝点击