复习动规

Posted manmanjiangqwq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复习动规相关的知识,希望对你有一定的参考价值。

今天早上换寝室,耽误了一些时间。还是继续复习动态规划。


 

单调队列优化dp

第一道,宝物筛选。一道多重背包优化题。如果用二进制优化很好做,但时间复杂度是O(nW*logm)。单调队列优化做法如下:

首先做出普通的多重背包的转移方程:f[j]=max{f[j-w*k]+v*k},w为重量,v为价值。

使得j=aw+b,即a=j/w,b=j%w。原方程可转化为f[j]=max{f[(a-k)*w+b]-(a-k)*v}+a*v。

这个时候我们就可以枚举b,将a-k看做整体,对于每个b都对f[(a-k)*w+b]-(a-k)*v做单调队列,再将a从大到小循环就可以了。

方程的转化比较难想到,代码的具体实现如下:

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=4e4+1e3;
 4 int n,W,p[N];
 5 long long f[N],q[N];
 6 long long Max(long long x,long long y){
 7     return x>y?x:y;
 8 }
 9 int Min(int x,int y){
10     return x<y?x:y;
11 }
12 int rd(){
13     int s=0,ff=1;
14     char w=getchar();
15     while(!isdigit(w))
16         ff^=w==-,w=getchar();
17     while(isdigit(w))
18         s=s*10+w-0,w=getchar();
19     return ff?s:-s;
20 }
21 int main(){
22     int v,w,m,l,r,a; long long x;
23     n=rd(); W=rd();
24     for(int i=1;i<=n;i++){
25         v=rd(),w=rd(),m=rd();
26         for(int b=0;b<w;b++){
27             l=1,r=0,a=(W-(W%w-b+w)%w)/w;
28             for(int k=1;k<=Min(a,m);k++){
29                 x=f[(a-k)*w+b]-(a-k)*v;
30                 while(l<=r&&q[r]<=x) r--;
31                 q[++r]=x,p[r]=a-k;
32             }
33             for(int j=W-(W%w-b+w)%w;j!=b;j-=w){
34                 a=j/w; while(p[l]>=a) l++;
35                 f[j]=Max(f[j],q[l]+a*v);
36                 if(a>=m+1) x=f[(a-m-1)*w+b]-(a-m-1)*v;
37                 while(l<=r&&q[r]<=x) r--;
38                 q[++r]=x,p[r]=a-m-1;
39             }
40         }
41     }
42     printf("%lld
",f[W]);
43     return 0;
44 }
宝物筛选

 


数据结构优化dp

第一道,"动态 DP"&动态树分治。剩下的大多数时间都花在上面了,头疼。

动态DP具体的思想和做法看这篇博客吧,个人觉得写的非常好。

我在这里讲下操作的具体实现(为了方便表达,把新定义的符号叫做乘号):

对于点x,它的权值要改为y,首先它树上的矩阵要先修改(别忘了权值要改)。

然后进入while循环,先求出Top[x](Top[x]为x所在重链的顶点)的f值,求法是对x这一整条重链即线段树上的连续一段求矩阵的连乘,因为叶子节点的f值等于g值,所以乘出来后矩阵第1行第1列就是f[i][0],第2行第1列就是f[i][1]。

再用树上x的矩阵更新线段树上x的矩阵,之后再求出Top[x]的f值。

再就可以用Top[x]的新旧f值修改树上“Top[x]的父亲”的矩阵(具体实现看代码,好理解的)。然后x=“Top[x]的父亲”。

然后while循环结束了,不用担心Top[x]的父亲也就是现在的x没有在线段树上更新,因为在下个循环会更新到的。

答案就是求出1的f值,取最大值输出即可。代码具体实现如下:

技术图片
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e5+1e4;
  4 int n,m,cnt,tot,h[N],d[N],f[N][2],g[N][2],fa[N],dep[N],siz[N],Son[N],Top[N],dfn[N],sfn[N],End[N];
  5 int Max(int x,int y){
  6     return x>y?x:y;
  7 }
  8 struct nod{
  9     int nxt,to;
 10 }a[N*2];
 11 struct Num{
 12     int p[3][3];
 13     Num(){
 14         memset(p,0,sizeof(p));
 15         p[2][2]=-1e8;
 16     }
 17     inline Num operator*(Num b){
 18         Num c;
 19         for(int i=1;i<=2;i++)
 20             for(int j=1;j<=2;j++)
 21                 for(int k=1;k<=2;k++)
 22                     c.p[i][j]=Max(c.p[i][j],p[i][k]+b.p[k][j]);
 23         return c;
 24     }
 25 }b[N*4],c[N],ans;
 26 int rd(){
 27     int s=0,ff=1;
 28     char w=getchar();
 29     while(!isdigit(w))
 30         ff^=w==-,w=getchar();
 31     while(isdigit(w))
 32         s=s*10+w-0,w=getchar();
 33     return ff?s:-s;
 34 }
 35 void Add(int x,int y){
 36     a[++cnt].nxt=h[x];
 37     a[cnt].to=y;
 38     h[x]=cnt;
 39 }
 40 void Dfs(int x,int w){ int y; siz[x]=1;
 41     for(int i=h[x];i;i=a[i].nxt){
 42         y=a[i].to;
 43         if(y==w) continue ; fa[y]=x;
 44         dep[y]=dep[x]+1; Dfs(y,x);
 45         siz[x]+=siz[y];
 46         if(siz[y]>siz[Son[x]])
 47             Son[x]=y;
 48     }
 49 }
 50 void Dfss(int x,int w){ int y;
 51     dfn[++tot]=x; sfn[x]=tot;
 52     if(Son[x])
 53         Top[Son[x]]=Top[x],Dfss(Son[x],x),End[x]=End[Son[x]];
 54     else End[x]=x;
 55     for(int i=h[x];i;i=a[i].nxt){
 56         y=a[i].to;
 57         if(y==w||y==Son[x]) continue ;
 58         Top[y]=y; Dfss(y,x);
 59     }
 60     
 61 }
 62 void Dfs_dp(int x,int w){ int y;
 63     f[x][0]=g[x][0]=0; f[x][1]=g[x][1]=d[x];
 64     for(int i=h[x];i;i=a[i].nxt){
 65         y=a[i].to; if(y==w) continue ; Dfs_dp(y,x);
 66         f[x][0]+=Max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
 67         if(y!=Son[x])
 68             g[x][0]+=Max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
 69     }
 70 }
 71 void Build(int x,int l,int r){
 72     if(l==r){
 73         b[x].p[1][1]=g[dfn[l]][0];
 74         b[x].p[1][2]=g[dfn[l]][0];
 75         b[x].p[2][1]=g[dfn[l]][1];
 76         b[x].p[2][2]=-1e8;
 77         c[dfn[l]]=b[x]; return ;
 78     }
 79     int mid=(l+r)>>1;
 80     Build(x<<1,l,mid);
 81     Build(x<<1|1,mid+1,r);
 82     b[x]=b[x<<1]*b[x<<1|1];
 83 }
 84 void Update(int x,int l,int r,int s){
 85     if(l==r){
 86         b[x]=c[dfn[l]]; return ;
 87     }
 88     int mid=(l+r)>>1;
 89     if(s<=mid) Update(x<<1,l,mid,s);
 90     else Update(x<<1|1,mid+1,r,s);
 91     b[x]=b[x<<1]*b[x<<1|1];
 92 }
 93 Num Query(int x,int l,int r,int L,int R){
 94     if(L<=l&&r<=R) return b[x];
 95     int mid=(l+r)>>1;
 96     if(mid>=R) return Query(x<<1,l,mid,L,R);
 97     else if(mid<L) return Query(x<<1|1,mid+1,r,L,R);
 98     else return Query(x<<1,l,mid,L,R)*Query(x<<1|1,mid+1,r,L,R);
 99 }
100 int main(){
101     int x,y,z; Num A,B;
102     n=rd(); m=rd();
103     for(int i=1;i<=n;i++)
104         d[i]=rd();
105     for(int i=1;i<n;i++)
106         x=rd(),y=rd(),Add(x,y),Add(y,x);
107     dep[1]=1; Dfs(1,0);
108     Top[1]=1; Dfss(1,0);
109     Dfs_dp(1,0); Build(1,1,n);
110     while(m--){
111         x=rd(),y=rd();
112         c[x].p[2][1]+=y-d[x]; d[x]=y;
113         while(x){
114             A=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
115             Update(1,1,n,sfn[x]);
116             B=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
117             x=fa[Top[x]];
118             c[x].p[1][1]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
119             c[x].p[1][2]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
120             c[x].p[2][1]+=B.p[1][1]-A.p[1][1];
121         }
122         A=Query(1,1,n,sfn[1],sfn[End[1]]);
123         printf("%d
",Max(A.p[1][1],A.p[2][1]));
124     }
125     return 0;
126 }
"动态DP"&动态树分治

 


今天就这样吧,看了几题比较简单就没写了,明天再说。

 

以上是关于复习动规的主要内容,如果未能解决你的问题,请参考以下文章

分享经典的动态规划问题

动态SQL基础概念复习(Javaweb作业5)

算法复习:递归

安卓复习8

安卓复习8

安卓复习8