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.
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.

浙公网安备 33010602011771号