• poj2436,poj3659,poj2430


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

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

    先说比较简单的;

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

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

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

     1 var dp:array[0..1,0..40000] of longint;  //表示所含病毒数为j的情况下最多可以挤多少只牛
     2     w,f:array[0..40000] of longint;
     3     s,ans,x,i,j,n,m,k,t,y,k1,k2:longint;
     4 
     5 function prework(x:longint):longint;  //预处理数字代表的病毒种类数目
     6   var t:longint;
     7   begin
     8     t:=0;
     9     while x<>0 do
    10     begin
    11       t:=t+x mod 2;
    12       x:=x div 2;
    13     end;
    14     exit(t);
    15   end;
    16 
    17 begin
    18   readln(t,m,k);
    19   s:=1 shl m-1;
    20   f[0]:=0;
    21   for i:=0 to s do 
    22   begin
    23     f[i]:=prework(i);
    24     dp[0,i]:=-1;
    25   end;
    26   for i:=1 to t do
    27   begin
    28     read(x);
    29     if x>k then readln
    30     else begin
    31       n:=n+1;
    32       w[n]:=0;
    33       for j:=1 to x do   //转化为二进制
    34       begin
    35         read(y);
    36         w[n]:=w[n]+1 shl (y-1);
    37       end;
    38     end;
    39   end;
    40   dp[0,0]:=0;
    41   k1:=1;
    42   k2:=0;
    43   ans:=0;
    44   for i:=1 to n do   //dp
    45   begin
    46     k1:=k1 xor 1;  //滚动数组
    47     k2:=k2 xor 1;
    48     dp[k2]:=dp[k1];
    49     for j:=0 to s do
    50     begin
    51       x:=j or w[i];  //位运算
    52       if (f[x]<=k) and (f[j]<=k) and (dp[k1,j]>=0) then
    53         dp[k2,x]:=max(dp[k2,x],dp[k1,j]+1);
    54       ans:=max(dp[k2,x],ans);
    55     end;
    56   end;
    57   writeln(ans);
    58 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)

      1 const max=30000001;
      2 var f:array[0..1,0..1010,0..4] of longint;
      3     a,b:array[0..1010] of longint;
      4     i,j,k1,k2,n,k,m,w,ans,t:longint;
      5 function findmin(t:longint):longint;
      6   var p,k:longint;
      7   begin
      8     p:=max;
      9     for k:=1 to 4 do
     10       p:=min(p,f[k1,j-t,k]);
     11     exit(p);
     12   end;
     13 procedure sort(l,r: longint); //按列排序,上下都有牛时1在前2在后,这样处理dp的时候方便多
     14   var i,j,x,y: longint;
     15   begin
     16     i:=l;
     17     j:=r;
     18     x:=a[(l+r) div 2];
     19     y:=b[(l+r) div 2];
     20     repeat
     21       while (a[i]<x) or (a[i]=x) and (b[i]<y) do inc(i);
     22       while (x<a[j]) or (a[j]=x) and (b[j]>y) do dec(j);
     23       if not(i>j) then
     24       begin
     25         swap(a[i],a[j]);
     26         swap(b[i],b[j]);
     27         inc(i);
     28         j:=j-1;
     29       end;
     30     until i>j;
     31     if l<j then sort(l,j);
     32     if i<r then sort(i,r);
     33   end;
     34 
     35 procedure doit1;
     36   begin
     37     f[k2,j,1]:=min(f[k2,j,1],f[k1,j,1]+2*(a[i]-a[i-1]));
     38     f[k2,j,1]:=min(findmin(1)+2,f[k2,j,1]);
     39   end;
     40 
     41 procedure doit2;
     42   var p:longint;
     43   begin
     44     f[k2,j,2]:=min(f[k1,j,2]+2*(a[i]-a[i-1]),f[k2,j,2]);
     45     p:=min(min(f[k1,j-1,2],f[k1,j-1,3]),f[k1,j-1,4]);
     46     f[k2,j,2]:=min(f[k2,j,2],p+a[i]-a[i-1]+1);
     47     if j-2>=0 then
     48       f[k2,j,2]:=min(f[k2,j,2],findmin(2)+2);
     49   end;
     50 
     51 procedure doit(x:longint);
     52   var p:longint;
     53   begin
     54     p:=min(f[k1,j,x],f[k1,j,2]);
     55     f[k2,j,x]:=min(f[k2,j,x],p+a[i]-a[i-1]);
     56     f[k2,j,x]:=min(f[k2,j,x],findmin(1)+1);
     57   end;
     58 
     59 begin
     60   readln(n,k,m);
     61   for i:=1 to n do
     62     readln(b[i],a[i]);
     63   sort(1,n);
     64   for i:=0 to k do  //初始化
     65     for j:=1 to 4 do
     66     begin
     67       f[0,i,j]:=max;
     68       f[1,i,j]:=max;
     69     end;
     70   i:=1;
     71   f[0,1,1]:=2;
     72   if a[i]=a[i+1] then
     73   begin
     74     i:=3;
     75     f[0,2,2]:=2;
     76   end
     77   else begin
     78     i:=2;
     79     if b[1]=1 then f[0,1,3]:=1
     80     else f[0,1,4]:=1;
     81   end;
     82   k1:=1;
     83   k2:=0;
     84   while i<=n do
     85   begin
     86     k1:=k1 xor 1; //滚动数组
     87     k2:=k2 xor 1;
     88     if a[i]=a[i+1] then w:=2 else w:=1;
     89     for j:=1 to k do
     90     begin
     91       for t:=1 to 4 do
     92         f[k2,j,t]:=max;
     93       doit1;  //做4个状态,方程式自己动手比划一下就明白了
     94       doit2;
     95       if w=1 then
     96       begin
     97         if b[i]=1 then
     98           doit(3)
     99         else doit(4);
    100       end;
    101     end;
    102     i:=i+w;
    103   end;
    104   ans:=max;
    105   for i:=1 to 4 do
    106     ans:=min(ans,f[k2,k,i]);
    107   writeln(ans);
    108 end.
    View Code

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

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

  • 相关阅读:
    字符编码乱码处理
    字典,元组,集合的使用
    三级菜单 ,求1
    运算符 if和while的使用
    if条件判断和while循环
    基本数据类型的结构和使用方法
    计算机基础部分
    计算机基础
    数据库之表查询,单表、多表,子查询
    google map API 学习
  • 原文地址:https://www.cnblogs.com/phile/p/4473288.html
Copyright © 2020-2023  润新知