poj2436,poj3659,poj2430

时间:2023-11-25 20:43:50

这两题都体现了dp的核心:状态

dp做多就发现,状态一设计出来,后面的什么都迎刃而解了(当然需要优化的还要动动脑筋);

先说比较简单的;

poj2436 由题得知病毒种数<=15很小,于是我们就容易想到将病每个牛携带的病毒抽象成15位的二进制数

0表示这种病毒不存在,1表示病毒存在,于是设cowi携带的病毒转化为二进制数为a,cowj为b

则如果挤cowi和cowj,那么所含病毒数为a or b,由此dp就显然了;

 var dp:array[..,..] of longint;  //表示所含病毒数为j的情况下最多可以挤多少只牛
    w,f:array[..] of longint;
    s,ans,x,i,j,n,m,k,t,y,k1,k2:longint; function prework(x:longint):longint;  //预处理数字代表的病毒种类数目
  var t:longint;
  begin
    t:=;
    while x<> do
    begin
      t:=t+x mod ;
      x:=x div ;
    end;
    exit(t);
  end; begin
  readln(t,m,k);
  s:= shl m-;
  f[]:=;
  for i:= to s do 
  begin
    f[i]:=prework(i);
    dp[,i]:=-;
  end;
  for i:= to t do
  begin
    read(x);
    if x>k then readln
    else begin
      n:=n+;
      w[n]:=;
      for j:= to x do   //转化为二进制
      begin
        read(y);
        w[n]:=w[n]+ shl (y-);
      end;
    end;
  end;
  dp[,]:=;
  k1:=;
  k2:=;
  ans:=;
  for i:= to n do   //dp
  begin
    k1:=k1 xor ;  //滚动数组
    k2:=k2 xor ;
    dp[k2]:=dp[k1];
    for j:= to s do
    begin
      x:=j or w[i];  //位运算
      if (f[x]<=k) and (f[j]<=k) and (dp[k1,j]>=) then
        dp[k2,x]:=max(dp[k2,x],dp[k1,j]+);
      ans:=max(dp[k2,x],ans);
    end;
  end;
  writeln(ans);
end.

poj2436

写的比较随意,有些地方还可以优化 O(n*2^m)是可以在1s出来的,实际813ms(悬)

poj3659是树的最小支配集

这题贪心也是正确方法,但这里我介绍的是treedp

对于当前节点i,容易想的两种状态f[i,1]表示在i节点建立发射塔(属于支配集)

所以它的孩子节点状态就随意了,每个子节点选个最小的状态就行了;

f[i,0]表示在i节点不建立发射塔,但i和其子树都被覆盖了

所以f[i,0]=signma(min(f[j,0],f[j,1]))-min(f[k,0]-f[k,1]); j表示i的所有孩子,而k表示其中有一个孩子一定要属于支配集

注意不要漏了一种情况:那就是i可以不被儿子覆盖而被它的父亲覆盖,并且它的子树都是全覆盖的

f[i,2]=signma(min(f[j,0],f[j,1]))

最后的ans=min(f[root,0],f[root,1]);

实现起来还是很容易的,dfs到叶子然后想上更新

值得注意的是,题目给出的相邻结点而非父子关系,所以用图的形式存然后以任意一点做treedp即可

poj2430是使用k个矩形覆盖所用奶牛的最小覆盖面积

同样要设计好状态

设f[i,j,k]表示到覆盖到第i只奶牛使用j个木板,k表示覆盖结尾的状态,显然有4种

1.上下都圈而且上下是属于同一次圈的
2.上下都圈而且上下是不属于同一次圈的,属于两次圈的
3.只圈上面的
4.只圈下面的

然后就轻松了

code:复杂度O(nk+nlog2n)

 const max=;
var f:array[..,..,..] of longint;
    a,b:array[..] of longint;
    i,j,k1,k2,n,k,m,w,ans,t:longint;
function findmin(t:longint):longint;
  var p,k:longint;
  begin
    p:=max;
    for k:= to do
      p:=min(p,f[k1,j-t,k]);
    exit(p);
  end;
procedure sort(l,r: longint); //按列排序,上下都有牛时1在前2在后,这样处理dp的时候方便多
  var i,j,x,y: longint;
  begin
    i:=l;
    j:=r;
    x:=a[(l+r) div ];
    y:=b[(l+r) div ];
    repeat
      while (a[i]<x) or (a[i]=x) and (b[i]<y) do inc(i);
      while (x<a[j]) or (a[j]=x) and (b[j]>y) do dec(j);
      if not(i>j) then
      begin
        swap(a[i],a[j]);
        swap(b[i],b[j]);
        inc(i);
        j:=j-;
      end;
    until i>j;
    if l<j then sort(l,j);
    if i<r then sort(i,r);
  end; procedure doit1;
  begin
    f[k2,j,]:=min(f[k2,j,],f[k1,j,]+*(a[i]-a[i-]));
    f[k2,j,]:=min(findmin()+,f[k2,j,]);
  end; procedure doit2;
  var p:longint;
  begin
    f[k2,j,]:=min(f[k1,j,]+*(a[i]-a[i-]),f[k2,j,]);
    p:=min(min(f[k1,j-,],f[k1,j-,]),f[k1,j-,]);
    f[k2,j,]:=min(f[k2,j,],p+a[i]-a[i-]+);
    if j->= then
      f[k2,j,]:=min(f[k2,j,],findmin()+);
  end; procedure doit(x:longint);
  var p:longint;
  begin
    p:=min(f[k1,j,x],f[k1,j,]);
    f[k2,j,x]:=min(f[k2,j,x],p+a[i]-a[i-]);
    f[k2,j,x]:=min(f[k2,j,x],findmin()+);
  end; begin
  readln(n,k,m);
  for i:= to n do
    readln(b[i],a[i]);
  sort(,n);
  for i:= to k do  //初始化
    for j:= to do
    begin
      f[,i,j]:=max;
      f[,i,j]:=max;
    end;
  i:=;
  f[,,]:=;
  if a[i]=a[i+] then
  begin
    i:=;
    f[,,]:=;
  end
  else begin
    i:=;
    if b[]= then f[,,]:=
    else f[,,]:=;
  end;
  k1:=;
  k2:=;
  while i<=n do
  begin
    k1:=k1 xor ; //滚动数组
    k2:=k2 xor ;
    if a[i]=a[i+] then w:= else w:=;
    for j:= to k do
    begin
      for t:= to do
        f[k2,j,t]:=max;
      doit1;  //做4个状态,方程式自己动手比划一下就明白了
      doit2;
      if w= then
      begin
        if b[i]= then
          doit()
        else doit();
      end;
    end;
    i:=i+w;
  end;
  ans:=max;
  for i:= to do
    ans:=min(ans,f[k2,k,i]);
  writeln(ans);
end.

上述三道题都是非常好的设计状态题,状态是用dp解决问题的关键

特别poj2430一设计出状态,dp立马迎刃而解