暑假集训第一周总结

      暑假集训第一周总结:


     学习内容:  区间dp,树形dp,泛化背包,概率dp,数位dp,kmp,线段树与树状数组,矩阵快速幂,一些STL函数和容器的使用,扫描线;

   

    1:区间dp     

      这类dp主要用来处理区间上求值问题  比如 最优三角形剖分问题  : 


     将一个凸多边形分成n个三角形   问划线的最短长度     zoj3537     poj 1179

                        for (i =0; i < n; ++i)
for (j = i +2; j < n; ++j)    //这里注意相邻点不需划线
cost[i][j] = cost[j][i] = Count(save[i],save[j]);
for (i =0; i < n; ++i) {
for (j =0; j < n; ++j)
dp[i][j] = INF;
dp[i][(i+1)%n] =0;    //仍然  相邻点dp值为0   这里编号1与编号n相邻
}
for (i = n -3; i >= 0; --i)//注意这三个for循环的顺序
for (j = i +2; j < n; ++j)//因为要保证在算dp[i][j]时dp[i][k]和dp[k][j]时已经计算,所以i为逆序,j要升序
for (k = i +1; k <= j - 1; ++k)
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);

      思想:类似于遍历每一种情况的暴力结合递归or递推   在三层for循环的过程中  已经计算出每两点之间划线的最小长度   dp[1][n]=dp[1][k]+dp[k][n]+cost[i][k]+cost[k][j];
                                            
  2:树形dp
     
     树形dp主要用来解决 取点时有前置条件  比如有前置点 比如与父亲节点不能同时出现  比如只在n个点取m个点     这时dp的状态一般为二维   
   
      dp[i][j] 通常表示 在以i为根节点的树下取j个点的值       dp[i][1],dp[i][0] 通常表示 在以i为根节点的树下 不取i的最大值和取了i的最大值

     典型例题   hdu 1520  hdu 1561   hdu 1054  hdu 2412   poj 1947

     树形dp的类型很多   如求能取到的最多点 能取到的最大价值   取n个点的最大价值
 

void bfs(int i)
{
   if(visit[i]) return;
    visit[i]=1;
    dp[i][1]=a[i];
   for(int x=head[i];x!=-1;x=e[x].next)
    {
       if(visit[e[x].to]!=1)
        {
            bfs(e[x].to);
            dp[i][1]+=dp[e[x].to][0];
            dp[i][0]+=max(dp[e[x].to][0],dp[e[x].to][1]);
        }
        
    }
}    //临接表存图的深搜    一直处理到子结点 然后逐层向上更新  最后得到需要的值 


临接表模版:  

struct data
{
   int to;
   int next;
}e[12010];
int head[6010],
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&p,&q);
           if(p==0&&q==0)break;
            e[top].to=p;
            e[top].next=head[q];
            head[q]=top++;
            
            e[top].to=q;
            e[top].next=head[p];
            head[p]=top++;
        }


void bfs(int i)
{
   if(visit[i]) return;
         visit[i]=1;
    dp[i][1]=a[i];
   for(int x=head[i];x!=-1;x=e[x].next)
    {
       if(visit[e[x].to]==0)
        {
            bfs(e[x].to);
          for(int j=m;j>=1;j--)
              for(int k=1;k<j;k++)
                  dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[e[x].to][k]);
        }
    }
}    //结合泛化背包的取点问题  注意三层循环顺序    也是泛化背包的顺序

  


3:泛化背包  
   
   多与树形dp结合  主要体现一种任务分配思想    eg:   有三种任务  每种任务投入时间不同收益不同  现时间确定  问如何安排活动能取得最大价值 

   hdu 1712    典型的泛化背包。

 for(i=1;i<=n;i++)
           for(j=m;j>=1;j--)
               for(k=j;k>=1;k--)
                dp[j]=max(dp[j],dp[j-k]+a[i][k]);


    dp[j]指消耗j点体力可以得到的收益     转移方程指   dp[j]可能由  dp[j-k]+第i种任务消耗k时间

4:   概率dp  

     接触到了两种类型  一种是求期望  一种是求步数;
    
    求期望需要正向推导  求步数反向推导    

    类似于  已知第一步走到x1-xn的概率  问走到xm的概率或期望  这时候需要正向递推  每一个xi的概率等于之前每个能走到xi的点的概率乘走到xi的概率  再求和
   
    另一种情况   已知现在在xm点  问从x1走到那个点的步数 这时候dp[xm]=0;   每个能到xm的点的步数等于1+走到xm的概率*dp[xm];   可形象理解成 走了一步后后面还有1/6概率走1步到达 4/6概率走2步到达  1/6概率走三步到达  最后一直推导起点。



    
for(i=n-1;i>=0;i--)
        {
           if(father[i]!=-1) dp[i]=dp[father[i]];
           else
            {
           for(j=1;j<=6;j++)
            {
               if(i+j<=n) dp[i]+=(double)dp[i+j]/6;
               else break;
            }
            dp[i]+=1.0;
            }
        }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值