dp

匿名 (未验证) 提交于 2019-12-02 23:49:02

树形dp:

思路:一道树形dp,以树的节点编号为dp的阶段,同时宴会当中某个人的状态只有两种,参加或不参加,所以用dp[ i ][ 0 ]表示 i 号节点不参加,dp[ i ][ 1 ]表示i号节点参加。树形dp,状态的转移,以子节点为前一阶段,父节点的当前阶段由上一阶段子节点推出。所以要先求出叶子节点的dp值(边界值),这就要采用递归的方式求解。状态转移方程就不写了

#include<bits/stdc++.h> using namespace std; int n; int h[6600]; vector<int> tr[6600]; int f[6600][2],v[6600];  void dp(int root) {      f[root][0]=0;     f[root][1]=h[root];     int len=(int)tr[root].size();     for(int j=0;j<len;j++)     {         int y=tr[root][j];         dp(y);         f[root][0]+=max(f[y][0],f[y][1]);         f[root][1]+=f[y][0];     } }   int main() {     cin>>n;     for(int i=1;i<=n;i++) scanf("%d",&h[i]);     int x,y;     for(int i=1;i<=n;i++) {         cin>>x>>y;         v[x]=1;         tr[y].push_back(x);     }      int root;     for(int i=1;i<=n;i++)     {         if(!v[i]){root=i;break;}     }      dp(root);     cout<<max(f[root][0],f[root][1])<<"\n";       return 0; }

环形dp:

环形dp求解方法有两种:1.先把环形dp当成线性dp求解。然后注意,线性dp变为环形dp后,多了哪些特殊情况,把这些特殊情况处理掉。所以这种方法意思就是,环形dp=线性dp+线性变环形后的多出来的特殊情况。

题意:某个星球上一天有N个小时,0点到1点为第一个小时,1点到2点为第二个小时,以此类推。并且再第 i 个小时睡觉能恢复 Ui 小时的体力。现在又一头牛每天要睡 B 个小时。但这 B 个小时不一定连续,也就是说其休息时间可能是几个小段。并且每一个小段第一个小时不能恢复体力。现在让你给这头安排一个休息计划,使得它每天恢复的体力最多。

思路:先把它按照线性来处理,就是不理会前一天的第N个小时和后一天的第1个小时是连续的。这样由于时间是增长的,选取时间作为dp的阶段,则当前牛只有两个状态要么睡觉,要么醒着。要想要实现最优子结构的状态转移,同时还要知道它已经休息了多长时间。所以用dp[ i ][ j ][ 0 ]表示前 i 小时已经休息了 j 个小时,并且当前是醒着的状态时恢复的最大体力。dp[ i ][ j ][ 1 ]表示当前前 i 个小时休息了 j 个小时,并且当前是睡觉状态恢复的最大体力。 状态转移方程不写了。这个时候dp的边界条件是 dp[ 1 ][ 0 ][ 0 ]=dp[ 1 ][ 1 ][ 1 ]=0; 而相比于环形,环形仅仅多了一种极端情况 dp[ 1 ][ 1 ][ 1 ]=U[ 1 ],所以环形情况下只要令初始dp值为它,再跑一次线性dp就可以了。

#include<iostream> #include<stdio.h> #include<cmath> #include<cstring> using namespace std; const int inf=0x3f3f3f3f; typedef long long ll; ll U[4000]; int N,B; ll ans1,ans2; ll dp[4000][2];  /*struct node {     int id;     int state; }D[4000][4000][2],D2[4000][4000][2];  void update1(int cur,int i,int j) {     if(dp[i-1][j][0]>dp[i-1][j][1])         {             dp[i][j][0]=dp[i-1][j][0];             if(cur==1)             {             D[i][j][0].id=i-1;D[i][j][0].state=0;             }             else             {             D2[i][j][0].id=i-1;D2[i][j][0].state=0;             }         }         else         {             dp[i][j][1]=dp[i-1][j][1];             if(cur==1)             {             D[i][j][0].id=i-1;D[i][j][0].state=1;             }             else             {                 D2[i][j][0].id=i-1;D2[i][j][0].state=1;             }         } }  void update2(int cur,int i,int j) {     //dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+U[i]);     if(dp[i-1][j-1][0]>dp[i-1][j-1][1]+U[i])     {         dp[i][j][1]=dp[i-1][j-1][0];         if(cur==1)         {         D[i][j][1].id=i-1;         D[i][j][1].state=0;         }         else         {           D2[i][j][1].id=i-1;           D2[i][j][1].state=0;         }     }     else     {         dp[i][j][1]=dp[i-1][j-1][1]+U[i];         if(cur==1)         {         D[i][j][1].id=i-1;         D[i][j][1].state=1;         }         else         {           D2[i][j][1].id=i-1;           D2[i][j][1].state=1;         }     }  } */  int main() {     cin>>N>>B;     for(int i=1;i<=N;i++)     {         cin>>U[i];     }      memset(dp,-inf,sizeof dp);     dp[0][0]=0;     dp[1][1]=0;      for(int i=2;i<=N;i++)      for(int j=i;j>=0;j--)     {         dp[j][0]=max(dp[j][0],dp[j][1]);         if(j-1>=0)         dp[j][1]=max(dp[j-1][0],dp[j-1][1]+U[i]);     }      ans1=max(dp[B][0],dp[B][1]);     memset(dp,-inf,sizeof dp);      dp[1][1]=U[1];     for(int i=2;i<=N;i++)      for(int j=i;j>=0;j--)     {         dp[j][0]=max(dp[j][0],dp[j][1]);         if(j-1>=0)         dp[j][1]=max(dp[j-1][0],dp[j-1][1]+U[i]);     }      ans2=dp[B][1];      cout<<max(ans1,ans2)<<"\n";      return 0; }

第二种处理方法:复制一遍要处理的序列加到第一条序列末尾。

状态 压缩dp:有的时候不仅需要由上一阶段的最优值来推下一阶段的最优值,并且下一阶段的某些状态与上一阶段的某些状态有依赖关系,所以这个时候我们不仅要保存上一阶段的最优值,而且还需要上一阶段的状态。这个时候我们把状态压缩成一个十进制的整数,十进制转化为二进制其 0 1的分布情况就代表了状态分布。

题意:给你一个N*M的棋盘,现在让你把它分割成若干个1*2的长方形。有多少种分割方案。

最短路:

题意:当前有M个任务,每个任务给定开始时间,和结束时间。小明按照一定的规则来做任务,即当前时刻若小明空闲,并且有任务开始,则分配给小明。若当前有多个任务,则选择其中一个任务。问怎样安排任务给小明,则小明的空闲时间最多。

思路:原本是dp,但是dp不会,题解也看不懂。把每个任务的开始时刻作为父节点,其任务结束时刻作为子节点,把之间相隔的时间作为边权值,但是这样的话都得到的图是很多个分裂开来的树。则把出度为0的节点即,任务结束的时刻与它的下一时刻的时间节点相连,但由于中间没有任务,所以把边权值设为0,这样就得到了一个带权的有环图,再环上跑一边dijkstra,得到最短路,最后再用总时间减去最短路就得到最多休息时间了。

但是这道题一开始把优先队列,写成一般队列了,还一直没找出错。。。

#include<bits/stdc++.h> using namespace std; const int maxn=20000; priority_queue< pair<int,int> > q;  int n,m; int cnt=0;  int head[maxn]; struct Edge{   int next;   int to;   int w; }edge[maxn];  void add(int u,int v,int w) {     edge[++cnt].next=head[u];     edge[cnt].to=v;     edge[cnt].w=w;     head[u]=cnt; }  int dis[maxn]; int vis[maxn]; void dijkstra() {     memset(dis,0x3f,sizeof dis);     dis[1]=0;     q.push(make_pair(0,1));     //q.push(1);     int now;     while(!q.empty())     {         now=q.top().second;         q.pop();         if(vis[now]) continue;         //vis[now]=false;         vis[now]=1;         for(int i=head[now];i;i=edge[i].next)         {             if(dis[edge[i].to]>dis[now]+edge[i].w)             {                 dis[edge[i].to]=dis[now]+edge[i].w;                  q.push(make_pair(-dis[edge[i].to],edge[i].to));                 //vis[edge[i].to]=1;             }         }      } }   int main() {     scanf("%d%d",&n,&m);     int x,y;     for(int i=1;i<=m;i++)     {        cin>>x>>y;        add(x,x+y,y);     }      for(int i=1;i<=n;i++)     {         if(!head[i])         {             add(i,i+1,0);         }     }      dijkstra();     cout<<n-dis[n+1];      return 0; }

dpP1140 相似基因

题意:给你一个两个DNA序列,问他们的最大相似度是多少。

#include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f;  int dp[150][150]; int x,y; string s1,s2; int char_num(char r) {     if(r=='A') return 1;     if(r=='C') return 2;     if(r=='G') return 3;     if(r=='T') return 4; }  int main() {      int  v[6][6]={        {0,0,0,0,0,0},        {0,5,-1,-2,-1,-3},        {0,-1,5,-3,-2,-4},        {0,-2,-3,5,-2,-2},        {0,-1,-2,-2,5,-1},        {0,-3,-4,-2,-1,0}     };      cin>>x>>s1;     cin>>y>>s2;      memset(dp,-inf,sizeof dp);      dp[0][0]=0;     for(int i=1;i<=x;i++)     {     //cout<<"---"<<s1[i-1]<<"\n";    // cout<<"+++"<<char_num(s1[i-1])<<"\n";     dp[i][0]=dp[i-1][0]+v[char_num(s1[i-1])][5];     }    for(int i=1;i<=y;i++)     dp[0][i]=dp[0][i-1]+v[5][char_num(s2[i-1])];      for(int i=1;i<=x;i++)      for(int j=1;j<=y;j++)     {         dp[i][j]=max(dp[i][j],dp[i][j-1]+v[5][char_num(s2[j-1])]);         dp[i][j]=max(dp[i][j],dp[i-1][j]+v[char_num(s1[i-1])][5]);         dp[i][j]=max(dp[i][j],dp[i-1][j-1]+v[char_num(s1[i-1])][char_num(s2[j-1])]);     }      cout<<dp[x][y];     return 0; }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!