ACM - 动态规划专题 题目整理
CodeForces 429B Working out
预处理出从四个顶点到某个位置的最大权值,再枚举相遇点,相遇的时候只有两种情况,取最优解即可。
#include<iostream> #include<vector> #include<cmath> #include<map> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<queue> #include<string> #define LL long long using namespace std; int n,m; ][]; ][],f2[][]; ][],f4[][]; bool judge(int x,int y) { <=x&&x<=n&&<=y&&y<=m; } int main() { scanf("%d%d",&n,&m); ; i<=n; ++i) ; j<=m; ++j) scanf("%d",&mat[i][j]); f1[][]=mat[][]; ; i<=n; ++i) ; j<=m; ++j) { &&j==) continue; ,j)) f1[i][j]=max(f1[i][j],f1[i-][j]+mat[i][j]); )) f1[i][j]=max(f1[i][j],f1[i][j-]+mat[i][j]); } f2[n][m]=mat[n][m]; ; --i) ; --j) { if(i==n&&j==m) continue; ,j)) f2[i][j]=max(f2[i][j],f2[i+][j]+mat[i][j]); )) f2[i][j]=max(f2[i][j],f2[i][j+]+mat[i][j]); } f3[n][]=mat[n][]; ; --i) ; j<=m; ++j) { ) continue; ,j)) f3[i][j]=max(f3[i][j],f3[i+][j]+mat[i][j]); )) f3[i][j]=max(f3[i][j],f3[i][j-]+mat[i][j]); } f4[][m]=mat[][m]; ; i<=n; ++i) ; --j) { &&j==m) continue; ,j)) f4[i][j]=max(f4[i][j],f4[i-][j]+mat[i][j]); )) f4[i][j]=max(f4[i][j],f4[i][j+]+mat[i][j]); } ; ; i<n; ++i) ; j<m; ++j) { ans=max(ans,f1[i-][j]+f2[i+][j]+f3[i][j-]+f4[i][j+]); ans=max(ans,f3[i+][j]+f4[i-][j]+f1[i][j-]+f2[i][j+]); } printf("%d\n",ans); ; }
UVA 10590 Boxes of Chocolates Again
此题为经典的整数划分问题。
O(n^2)的复杂度:dp[i][j]=dp[i][j-1]+dp[i-1][j]。另外用大数,可能会超时。
另有他法。
import java.util.*; import java.math.BigInteger; import java.io.*; public class Main { public static void main(String[] args) { BigInteger[] f= new BigInteger[5001]; for(int i=1;i<=5000;++i) f[i]=new BigInteger("0"); f[0]=new BigInteger("1"); for(int i=1;i<=5000;++i) { for(int j=1;j<=i;++j) { int k1=i-j*(3*j-1)/2,k2=i-j*(3*j+1)/2; if(k1<0&&k2<0) break; BigInteger t=new BigInteger("0"); if(k1>=0) t=t.add(f[k1]); if(k2>=0) t=t.add(f[k2]); if(j%2==0) t=t.multiply(new BigInteger ("-1")); f[i]=f[i].add(t); } } Scanner in = new Scanner(System.in); while(in.hasNextInt()) { int n=in.nextInt(); System.out.println(f[n]); } } }
POJ 2127 Greatest Common Increasing Subsequence
此题比较容易想到一个O(n^4)的算法。
具体方法是首先用两个嵌套的循环遍历数组a和数组b,当a[i]==b[j]时,寻找在数组a[1……i-1],b[1……j-1]之间,满足a[i']==b[j']且a[i']<a[i]的dp[i'][j']的最大值,这样需要两个嵌套的循环来实现。方程是dp[i][j]=max{dp[i'][j']|a[i]==b[j],a[i']==b[j'],a[i']<a[i],1<=i'<i,1<=j'<j}。但是这个题会超时。
试着优化这个算法,在状态转移的时候我们要寻找一个的状态是,数值上小于当前的数组元素,而且dp[][]要最大。在上述算法中,状态转移时枚举i‘、j'只是为了确定当前状态存在。我们可以把所有以j'为最后一个元素的最长公共上升序列的最大值保存在数组opt[j']中,状态转移时只需要遍历opt[]数组就行。
实际上,对于每个a[i],我们只需要找opt[j']中对应满足b[j']<a[i],opt[j']的最大值即可。这里对最大值的维护可以与遍历j同步进行。复杂度降为O(n^2)。
O(n^3):
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> using namespace std; ][],opt[]; ],b[]; pair<][]; ]; bool fir; void output(int x,int y) { if(!x&&!y) return ; output(pre[x][y].first,pre[x][y].second); if(!fir) { fir=true; printf("%d",a[x]); } else printf(" %d",a[x]); } int main() { int s1,s2; while(scanf("%d",&s1)!=EOF) { ; i<=s1; ++i ) scanf("%d",&a[i]); scanf("%d",&s2); ; j<=s2; ++j) scanf("%d",&b[j]); memset(dp,,sizeof(dp)); memset(opt,,sizeof(opt)); ; pair<int,int> ed; ; i<=s1; ++i) ; j<=s2; ++j) { if(a[i]==b[j]) { ; ; k<j; ++k) if(b[k]<b[j]&&opt[maxn]<opt[k]) maxn=k; dp[i][j]=opt[maxn]+; pre[i][j].first=from[maxn]; pre[i][j].second=maxn; if(ans<dp[i][j]) { ans=dp[i][j]; ed.first=i; ed.second=j; } } if(opt[j]<dp[i][j]) { opt[j]=dp[i][j]; from[j]=i; } } printf("%d\n",ans); if(ans) { fir=false; output(ed.first,ed.second); printf("\n"); } } ; }
O(n^2):
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> using namespace std; ][],opt[]; ],b[]; pair<][]; ]; bool fir; void output(int x,int y) { if(!x&&!y) return ; output(pre[x][y].first,pre[x][y].second); if(!fir) { fir=true; printf("%d",a[x]); } else printf(" %d",a[x]); } int main() { int s1,s2; while(scanf("%d",&s1)!=EOF) { ; i<=s1; ++i ) scanf("%d",&a[i]); scanf("%d",&s2); ; j<=s2; ++j) scanf("%d",&b[j]); memset(dp,,sizeof(dp)); memset(opt,,sizeof(opt)); ; pair<int,int> ed; ; i<=s1; ++i) { ; ; j<=s2; ++j) { if(b[j]<a[i]&&opt[maxn]<opt[j]) maxn=j; if(b[j]==a[i]) { dp[i][j]=opt[maxn]+; pre[i][j].first=from[maxn]; pre[i][j].second=maxn; if(ans<dp[i][j]) { ans=dp[i][j]; ed.first=i; ed.second=j; } } if(opt[j]<dp[i][j]) { opt[j]=dp[i][j]; from[j]=i; } } } printf("%d\n",ans); if(ans) { fir=false; output(ed.first,ed.second); printf("\n"); } } ; }
POJ 2677 Tour
双调欧几里得旅行商问题。
http://blog.csdn.net/xiajun07061225/article/details/8092247
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> using namespace std; struct Point { int x,y; bool operator <( const Point &p) const { return x<p.x; } }; double distan(Point &a,Point &b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } ][]; Point p[]; int main() { int n; while(scanf("%d",&n)!=EOF) { vector<Point> vec; ; i<=n; ++i) { scanf("%d%d",&p[i].x,&p[i].y); } sort(p+,p++n); memset(dp,0x7f,sizeof(dp)); dp[][]=; ; j<=n; ++j) for(int i=j; i<=n; ++i) { &&i==) continue; if(i==j) { ; k<i; ++k) dp[i][j]=min(dp[i][j],dp[i][k]+distan(p[k],p[i])); } ==j) { ; k<=j; ++k) dp[i][j]=min(dp[i][j],dp[j][k]+distan(p[k],p[i])); } )dp[i][j]=dp[i-][j]+distan(p[i],p[i-]); } printf("%.2f\n",dp[n][n]); } ; }
POJ 3375 Network Connection
充分挖掘题目条件,利用电脑i最优解一定是连接[j-n,j+n]范围内的接口,j是离i最近的接口。
优化为O(n^2)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #define ll long long using namespace std; int distan(int x,int y) { return abs(x-y); } ],b[]; ][]; pair<]; int main() { int m,n; while(scanf("%d%d",&m,&n)!=EOF) { ; i<=m; ++i ) scanf("%d",&a[i]); ; i<=n; ++i ) scanf("%d",&b[i]); sort(a+,a++m); sort(b+,b++n); ; i<=n; ++i ) { ,a++m,b[i])-(a+); seg[i].first=max(i,p-n-); } memset(dp,,sizeof(dp)); ; i<=m; ++i) dp[][i]=distan(b[],a[i]); ; i<=n; ++i) { ].first; )&][k]; for(int j=seg[i].first; j<=seg[i].second; ++j) { ,seg[i-].second); while(k<lmin) { k++; minn=min(minn,dp[(i+)&][k]); } dp[i&][j]=minn+distan(b[i],a[j]); } } int ans=0x7fffffff; ][i]); printf("%d\n",ans); } ; }
POJ 3612 Telephone Wire
充分利用状态转移方程来优化转移部分。使复杂度降维O(n*m)。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #define ll long long using namespace std; ][]; ]; int calc(int i,int j) { return (j-h[i])*(j-h[i]); } int main() { int n,c; while(scanf("%d%d",&n,&c)!=EOF) { ; i<=n; ++i) scanf("%d",&h[i]); ; ; i<=n; ++i) maxn=max(maxn,h[i]); memset(dp,0x7f,sizeof(dp)); ]; i<=maxn; ++i) dp[][i]=(i-h[])*(i-h[]); ; i<=n; ++i) { for(int j=h[i]; j<=maxn; ++j) dp[i&][j]=dp[(i+)&][h[i-]]+abs(j-h[i-])*c+calc(i,j); int k,minn; k=minn=h[i-]; for(int j=h[i]; j<=maxn; ++j) { while(k<=j) { )&][minn]-minn*c>dp[(i+)&][k]-k*c) minn=k; k++; } if(minn<=j) dp[i&][j]=min(dp[i&][j],dp[(i+)&][minn]-minn*c+j*c+calc(i,j)); } k=minn=maxn; for(int j=maxn; j>=h[i]; --j) { ]) { )&][minn]+minn*c>dp[(i+)&][k]+k*c) minn=k; k--; } if(j>=minn) dp[i&][j]=min(dp[i&][j],dp[(i+)&][minn]+minn*c-j*c+calc(i,j)); } } int ans=0x7fffffff; for(int i=h[n]; i<=maxn; ++i) ans=min(ans,dp[n&][i]); printf("%d\n",ans); } ; }
背包类DP:
背包问题是一类非常经典的DP。很多DP的方程都是和背包相似的,这里总结一些类似背包的DP。
ZOJ 2711 Regular Words
设dp[i][j][k]表示前i个字符中有j个A,k个B,(i-j-k)个C时候的符合个数。然后分别考虑每次加A、B、C时候的状态转移,注意要判断状态是否合法。结果要用大数。
import java.math.BigInteger; import java.util.Scanner; public class Main { public static boolean judge(int a,int b,int c) { &&b>=&&c>=&&a>=b&&b>=c) return true; else return false; } public static void main(String[] args) { Scanner in = new Scanner(System.in); BigInteger[][][] dp = ][][]; ; i <= * ; ++i) dp[i][][] = "); ; dp[][][] = "); ; i <= * n; ++i) { ; j <= n; ++j) ; k <= j; ++k) { dp[i][j][k]="); , k, i - - (j - ) - k)) dp[i][j][k] = dp[i][j][k].add(dp[i - ][j - ][k]); , i - - j - (k - ))) dp[i][j][k] = dp[i][j][k].add(dp[i - ][j][k - ]); - j - k)) dp[i][j][k] = dp[i][j][k].add(dp[i - ][j][k]); } } while (in.hasNextInt()) { int m = in.nextInt(); System. * m][m][m]); System.out.println(); } } }
POJ 1276 Cash Machine
很经典的多重背包。需要优化。可以使用二进制分解的方法。考虑最朴素的算法中,假设每个物品选ki个为最优,而在二进制1,2,4.。。2^k可以表示0到2^(k+1)-1的所有整数,自然也包括ki,而这只有lgm个数,所以可以考虑用这nlgm个数做01背包。另外可以用单调队列来优化。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<vector> #define inf -2139062144 #define ll long long using namespace std; ],value[]; vector<int> cost; ]; int main() { int C,n; while(scanf("%d%d",&C,&n)!=EOF) { ; i<=n; ++i) scanf("%d%d",&coin[i],&value[i]); cost.clear(); ; i<=n; ++i) { ; coin[i]>; k<<=) { k=min(k,coin[i]); cost.push_back(k*value[i]); coin[i]-=k; } } memset(dp,,sizeof(dp)); ; i<cost.size(); ++i) { for(int j=C; j>=cost[i]; --j) dp[j]=max(dp[j],dp[j-cost[i]]+cost[i]); } printf("%d\n",dp[C]); } ; }
状态压缩:
HDU 4856 Tunnels
先BFS求两两之间最短路,再用状态压缩DP求最优解。这里一定要注意不可达的情况。
#include<iostream> #include<vector> #include<map> #include<queue> #include<algorithm> #include<cstring> #include<iostream> #include<cstdio> #include<cstdlib> #define INF 2139062143 using namespace std; vector<][]; ][]; int N,n; struct Node { int sx,sy,ex,ey; Node(int a,int b,int c,int d):sx(a),sy(b),ex(c),ey(d) {} }; vector<Node> vec; ][]; ][]; ][]= {{,},{-,},{,},{,-}}; bool judge(int x,int y) { <=x&&x<=N&&<=y&&y<=N; } void bfs(int x,int y,int c) { ][]= {}; queue< pair<int,int> > que; que.push(pair<int,int>(x,y)); while(!que.empty()) { pair<int,int> p=que.front(); que.pop(); ; i<mp[p.first][p.second].size(); ++i) dist[c][mp[p.first][p.second][i]]=min(step[p.first][p.second],dist[c][mp[p.first][p.second][i]]); ; i<; ++i) { ],ny=p.second+move[i][]; if(!judge(nx,ny)||step[nx][ny]||grid[nx][ny]=='#') continue; step[nx][ny]=step[p.first][p.second]+; que.push(pair<int,int>(nx,ny)); } } } ][]; ][]; int dp(int a,int S) { ) ; if(vis[a][S]) return f[a][S]; vis[a][S]=true; f[a][S]=INF; ; i<n; ++i) { <<i)) { <<i))!=INF&&dist[i][a]!=INF) f[a][S]=min(f[a][S],dp(i,S^(<<i))+dist[i][a]); } } return f[a][S]; } void clear() { ; i<=N; ++i) ; j<=N; ++j) mp[i][j].clear(); vec.clear(); memset(vis,,sizeof(vis)); memset(visit,,sizeof(visit)); memset(dist,0x7f,sizeof(dist)); } int main() { while(scanf("%d%d",&N,&n)!=EOF) { clear(); ; i<=N; ++i) scanf(); ; i<n; ++i) { int sx,sy,ex,ey; scanf("%d%d%d%d",&sx,&sy,&ex,&ey); vec.push_back(Node (sx,sy,ex,ey)); mp[sx][sy].push_back(i); } bool ok=true; ; i<n&&ok; ++i) bfs(vec[i].ex,vec[i].ey,i); int ans=INF; ; i<n; ++i) ans=min(ans,dp(i,((<<n)-)^(<<i))); if(ans>=INF)puts("-1"); else printf("%d\n",ans); } ; }
POJ 2686 Traveling by Stagecoach
比较简单的状态压缩DP。注意判断两点是否联通和,某点是否可达的情况。最好另外开一个bool数组来判断记忆化。
#include<iostream> #include<vector> #include<map> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<string> #define ll long long #define INF 1000000000 using namespace std; ][<<]; ][<<]; ][],t[]; int n,m,A,B; double dp(int pos,int T) { ; if(vis[pos][T]) return f[pos][T]; vis[pos][T]=true; f[pos][T]=INF; ; i<m; ++i) if(pos!=i&&g[pos][i]!=INF) { ; j<n; ++j) <<j))&&dp(i,T^(<<j))!=INF) { f[pos][T]=min(f[pos][T],dp(i,T^(<<j))+g[pos][i]/t[j]); } } return f[pos][T]; } void init() { memset(vis,,sizeof(vis)); ; i<m; ++i) for(int j=i; j<m; ++j) g[i][j]=g[j][i]=INF; } int main() { int p; while(scanf("%d%d%d%d%d",&n,&m,&p,&A,&B)!=EOF) { if(!n&&!m&&!p&&!A&&!B) break; A--; B--; init(); ; i<n; ++i) scanf("%lf",&t[i]); while(p--) { int a,b; double c; scanf("%d%d%lf",&a,&b,&c); a--; b--; g[a][b]=g[b][a]=c; } <<n)-)!=INF) printf(<<n)-)); else puts("Impossible"); } ; } //c++ 要用lf //g++要用f
POJ 3254 Corn Fields
状态压缩,分别枚举第i和第i+1行的状态,判断是否合法及有相邻,然后从第i行的状态转移到第i+1行即可。很简单。
#include<iostream> #include<vector> #include<cmath> #include<map> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<queue> #include<cmath> #include<set> #include<string> #define mod 100000000 #define LL long long using namespace std; int m,n; ][]; bool judgeSafeState(int r,int s) { ; i<n; ++i) <<i))&&!mat[r][i]) return false; return true; } bool judgeAttack2(int st,int ed) { ; i<n; ++i) <<i))&&(ed&(<<i))) return false; return true; } bool judgeAttack1(int s) { ; i<n; ++i) { <n&&(s&(<<(i+)))&&(s&(<<i))) return false; } return true; } LL dp[][]; int main() { scanf("%d%d",&m,&n); ; i<=m; ++i) ; j<n; ++j) scanf("%d",&mat[i][j]); dp[][]=; LL ans=; ; i<=m; ++i) { ; j<(<<n); ++j) if(judgeSafeState(i,j)&&judgeAttack1(j)) { ) dp[i][j]=; else { ; k<(<<n); ++k) ,k)&&judgeAttack1(k)&&judgeAttack2(j,k)) { dp[i][j]+=dp[i-][k]; dp[i][j]%=mod; } } if(i==m) { ans+=dp[i][j]; ans%=mod; } } } printf("%lld\n",ans); ; }
数位DP:
HDU 3555
先求不含49的,再减去。记忆化搜索实现,比递推方便。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define ll long long #define Maxn 20000 using namespace std; ll dp[][]; ]; ll dfs(int pos,int num,bool flag) { ) ; ) return dp[pos][num]; ; ll res=; ; i<=end; ++i) &&i==)) res+=dfs(pos-,i,flag&&i==end); if(!flag) dp[pos][num]=res; return res; } int main() { memset(dp,-,sizeof(dp)); int T; scanf("%d",&T); while(T--) { ll n,t; scanf("%I64d",&n); t=n; ; while(t) { bits[len++]=t%; t/=; } printf(,,)+); } ; }
HDU 4734 F(x)
dp[i][j]表示后i位小于j的数量。由于F(x)值并不大,所以可以存在状态里面。
dp方程:dp[i][j]=sum{dp[i-1][j-v[k]]} v[k]表示第i位取k时对于的值
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #define MAXN 10005 using namespace std; ][MAXN]; ]; int dp(int pos,int num,bool flag) { ) ; ) ; ) return f[pos][num]; ; ; ; i<=end; ++i) ans+=dp(pos-,num-(i<<pos),flag&&i==end); if(!flag) f[pos][num]=ans; return ans; } int getFx(int A) { ,l=; while(A) { res+=A%*(<<l); l++; A/=; } return res; } int setDigit(int B) { ; while(B) { digit[len++]=B%; B/=; } return len; } int main() { memset(f,-,sizeof(f)); ; scanf("%d",&T); while(T--) { int A,B; scanf("%d%d",&A,&B); printf(,getFx(A),)); } ; }
HDU 3652 B-number
dp[i][j][k][vis]表示第i位以j开头模13余k,是否出现过13的,符合数目。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define ll long long #define Maxn 20000 using namespace std; ][][][]; ]; int dp(int pos,int num,int mod,bool vis,bool flag) { ) return vis&&!mod; ) return f[pos][num][mod][vis]; ; ; ; i<=end; ++i) ans+=dp(pos-,i,(mod*+i)%,vis||(num==&&i==),flag&&(i==end)); if(!flag) f[pos][num][mod][vis]=ans; return ans; } int main() { int n; memset(f,-,sizeof(f)); while(scanf("%d",&n)!=EOF) { ; while(n) { digit[len++]=n%; n/=; } printf(,,,,)); } ; }
UVa 11361 Investigating Div-Sum Property
经典数位DP。问某区间内同时满足自身可被k整除和所有位数字和可被k整除的数字个数。
挖掘题意,发现所有位数字和不可能超过100。所以如果k>=100答案肯定是0。根据这个dp方程状态数目也将大大减少。
dp[i][j][k]=sum{dp[i-1][(j*10+j`)%K][(k+k`)%K]}
#include<iostream> #include<vector> #include<map> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<string> #define ll long long using namespace std; ][][]; ]; int K; int dp(int pos,int amod,int smod,bool flag) { ) return !amod&&!smod; ||smod<) ; ) return f[pos][amod][smod]; ; ; ; i<=end; ++i) ans+=dp(pos-,(i+amod)%K,(smod*+i)%K,flag&&i==end); if(!flag) f[pos][amod][smod]=ans; return ans; } int calc(int val) { ; while(val) { digit[len++]=val%; val/=; } memset(f,-,sizeof(f)); ,,,); } int main() { int T; scanf("%d",&T); while(T--) { int a,b; scanf("%d%d%d",&a,&b,&K); ) printf("0\n"); )); } ; }
POJ 2282 The Counting Problem
经典数位dp,问每个数字分别出现多少次。注意判断前导零。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<vector> using namespace std; ]; ]; ]; int var; int dp(int cur,int num,int val,bool fst,bool lst,int number) { ) ; ) return f[cur]; ; ; ; i<=end; ++i) { if(i==val) { if(i==end&&lst) ans+=(number%p[cur]+)+dp(cur-,i,val,i==&&fst,i==end&&lst,number); &&fst) ans+=dp(cur-,i,val,i==&&fst,i==end&&lst,number); else ans+=p[cur]+dp(cur-,i,val,i==&&fst,i==end&&lst,number); } ,i,val,i==&&fst,i==end&&lst,number); } if(!fst&&!lst) f[cur]=ans; return ans; } void solve(int *arr,int num) { ; int var=num; while(num) { digit[len++]=num%; num/=; } ; i<=; ++i) { memset(f,-,sizeof(f)); arr[i]=dp(len-,-,i,,,var); } } int main() { p[]=; ; i<=; ++i) p[i]=p[i-]*; int a,b; while(scanf("%d%d",&a,&b)!=EOF) { if(!a&&!b) break; if(a>b)swap(a,b); ],y[]; solve(x,a-); solve(y,b); ; i<; ++i) if(!i) printf("%d",y[i]-x[i]); else printf(" %d",y[i]-x[i]); printf("\n"); } ; }
HDU 4507 吉哥系列故事——恨7不成妻
分别维护多个信息,最后组成平方和。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<vector> #define LL long long #include<sstream> using namespace std; const LL mod=1000000007L; ]; struct State { //符合条件的个数,前几位数字和,2*xy项和,平方和 LL sumZero,sumOne,sumTwo,sumThree,sumFour; State(LL z=,LL a=,LL b=,LL c=): sumZero(z),sumOne(a),sumTwo(b),sumThree(c) {} ,LL a=,LL b=,LL c=) { sumZero=z,sumOne=a,sumTwo=b,sumThree=c; } }; LL p[]; ][][][]; State f[][][][]; State dp(int cur,int num,int ssum,int asum,bool lst) { ) { if(ssum&&asum) ,num,,num*num); else ,-,-,-); } if(!lst&&vis[cur][num][ssum][asum]) return f[cur][num][ssum][asum]; vis[cur][num][ssum][asum]=true; ; LL ansZ=,ansA=,ansB=,ansC=; LL a=num*p[cur+]%mod,c=(p[cur+]%mod*num)%mod; c=c*c%mod; ; i<=end; ++i) ) { State next=dp(cur-,i,(ssum+i)%,(asum*+i)%,lst&&i==end); ) continue; ansZ+=next.sumZero; ansA+=a*next.sumZero+next.sumOne; ansB+=(*a*next.sumOne+next.sumTwo);//*next.sumZero; ansC+=c*next.sumZero+next.sumThree; ansZ%=mod; ansA%=mod; ansB%=mod; ansC%=mod; } if(!lst) f[cur][num][ssum][asum].set(ansZ,ansA,ansB,ansC); return State(ansZ,ansA,ansB,ansC); } LL solve(LL val ) { ) ; ; while(val) { digit[len++]=val%; val/=; } memset(vis,,sizeof(vis)); State s=dp(len-,,,,); return (s.sumTwo+s.sumThree)%mod; } int main() { p[]=; ; i<=; ++i) p[i]=p[i-]*; int T; scanf("%d",&T); while(T--) { LL a,b; scanf("%I64d%I64d",&a,&b); printf()+mod)%mod); } ; }
LightOJ 1032 Fast Bit Calculations
简单数位DP。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #define LL long long using namespace std; int n; LL f[][]; ]; LL dp(int pos,int num,bool lst) { ) ; ) return f[pos][num]; LL ans=; ; ;i<=end;++i) { ans+=dp(pos-,i,i==end&&lst); &&i==num) ans+=(lst?n%(<<pos)+:(<<pos)); } if(!lst) f[pos][num]=ans; return ans; } LL solve(int val) { ; while(val) { digit[len++]=val%; val/=; } memset(f,-,sizeof(f)); ,,); } int main() { ; scanf("%d",&T); while(T--) { scanf("%d",&n); printf("Case %d: %lld\n",++kase,solve(n)); } ; }
树形DP:
URAL 1018 Binary Apple Tree
简单树形DP。dp[i][j]表示以i为根节点恰好有j条边时的最大权值。
dp[i][j]=max{dp[i][j],dp[lc][k]+cost[lc]+dp[rc][j-2-k]+cost[rc]}当同时从两个孩子转移时;
dp[i][j]=max{dp[i][j],dp[c][n-1]+cost[c]}当从一个孩子转移时。
记忆化搜索实现,注意方向。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<iostream> #define inf -100000000 #define LL long long using namespace std; int N,Q; struct Edge { int to,cost; Edge(int a,int b):to(a),cost(b) {} }; vector<Edge> g[]; ][]; int dp(int rt,int n,int fa) { ) ; ) return f[rt][n]; f[rt][n]=inf; ) { ; i<g[rt].size(); ++i) ,rt)!=inf) f[rt][n]=max(f[rt][n],dp(g[rt][i].to,n-,rt)+g[rt][i].cost); } ) { ; i<g[rt].size(); ++i) if(fa!=g[rt][i].to) { ; j<g[rt].size(); ++j) if(fa!=g[rt][j].to) { ; k<=n-; ++k) -k,rt)!=inf) f[rt][n]=max(f[rt][n],dp(g[rt][i].to,k,rt)+g[rt][i].cost+dp(g[rt][j].to,n--k,rt)+g[rt][j].cost); } } } return f[rt][n]; } int main() { while(scanf("%d%d",&N,&Q)!=EOF) { ; i<=N; ++i) g[i].clear(); ; i<N; ++i) { int a,b,c; scanf("%d%d%d",&a,&b,&c); g[a].push_back(Edge(b,c)); g[b].push_back(Edge(a,c)); } memset(f,-,sizeof(f)); printf(,Q,-)); } ; }
斜率优化:
HDU 3507 Print Article
单调队列维护斜率。
#include <cstdio> #include <cstring> #include <iostream> #include <deque> #include <algorithm> using namespace std; ]; ]; int getUp(int a,int b) { return dp[a]+sum[a]*sum[a]-(dp[b]+sum[b]*sum[b]); } int getDown(int a,int b) { *(sum[a]-sum[b]); } int main() { int n,M; while(scanf("%d%d",&n,&M)!=EOF) { ; i<=n; ++i) { scanf("%d",&sum[i]); sum[i]=sum[i]+sum[i-]; } deque<int> dq; dq.push_back(); ; i<=n; ++i) { &&getUp(dq[],dq[])<=sum[i]*getDown(dq[],dq[])) dq.pop_front(); dp[i]=dp[dq[]]+(sum[i]-sum[dq[]])*(sum[i]-sum[dq[]])+M; &&getUp(i,dq[dq.size()-])*getDown(dq[dq.size()-],dq[dq.size()-])<=getDown(i,dq[dq.size()-])*getUp(dq[dq.size()-],dq[dq.size()-])) dq.pop_back(); dq.push_back(i); } printf("%d\n",dp[n]); } ; }
HDU 4258 Covered Walkway
跟上个题很类似,但是注意是要覆盖点,不是所有区间,所以状态转移方程略有不同。体现在代码里可以先入队,再排除非最优解。
#include<iostream> #include<vector> #include<cmath> #include<map> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<queue> #include<cmath> #include<set> #include<string> #define LL long long using namespace std; LL dp[]; LL sum[]; inline LL getUp(int a,int b) { ]+sum[a]*sum[a]-(dp[b-]+sum[b]*sum[b]); } inline LL getDown(int a,int b) { *(sum[a]-sum[b]); } int main() { int n,C; while(scanf("%d%d",&n,&C)!=EOF) { if(!n&&!C) break; ; i<=n; ++i) scanf("%I64d",&sum[i]); deque<int> que; que.push_back(); ; i<=n; ++i) { &&getUp(i,que[que.size()-])*getDown(que[que.size()-],que[que.size()-])<=getUp(que[que.size()-],que[que.size()-])*getDown(i,que[que.size()-])) que.pop_back(); que.push_back(i); &&getUp(que[],que[])<=sum[i]*getDown(que[],que[])) que.pop_front(); dp[i]=dp[que[]-]+(sum[i]-sum[que[]])*(sum[i]-sum[que[]])+C; } printf("%I64d\n",dp[n]); } ; }
下面是一些经典的优化DP。
单调队列优化DP:
vijos 1243
URAL - 1031
poj 3017
HYSBZ - 1010
HYSBZ 1499
斜率优化DP:
HYSBZ 1096
HYSBZ 1492 货币兑换Cash
ACM - 动态规划专题 题目整理的更多相关文章
- NOIP2018提高组金牌训练营——动态规划专题
NOIP2018提高组金牌训练营——动态规划专题 https://www.51nod.com/Live/LiveDescription.html#!#liveId=19 多重背包 二进制优化转化成01 ...
- ACM个人零散知识点整理
ACM个人零散知识点整理 杂项: 1.输入输出外挂 //读入优化 int 整数 inline int read(){ int x=0,f=1; char ch=getchar(); while(ch& ...
- Noip往年题目整理
Noip往年题目整理 张炳琪 一.历年题目 按时间倒序排序 年份 T1知识点 T2知识点 T3知识点 得分 总体 2016day1 模拟 Lca,树上差分 期望dp 144 挺难的一套题目,偏思维难度 ...
- Problem C: 动态规划基础题目之数字三角形
Problem C: 动态规划基础题目之数字三角形 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 208 Solved: 139[Submit][Sta ...
- 正睿国庆DAY2动态规划专题
正睿国庆DAY2动态规划专题 排列-例题 1~n 的排列个数,每个数要么比旁边两个大,要么比旁边两个小 \(f[i][j]\) 填了前i个数,未填的数有\(j\)个比第\(i\)个小,是波峰 \(g[ ...
- NOIp初赛题目整理
NOIp初赛题目整理 这个 blog 用来整理扶苏准备第一轮 csp 时所做的与 csp 没 有 关 系 的历年 noip-J/S 初赛题目,记录了一些我从不知道的细碎知识点,还有一些憨憨题目,不定期 ...
- 【ACM/ICPC2013】树形动态规划专题
前言:按照计划,昨天应该是完成树形DP7题和二分图.最大流基础专题,但是由于我智商实在拙计,一直在理解树形DP的思想,所以第二个专题只能顺延到今天了.但是昨天把树形DP弄了个5成懂我是很高兴的!下面我 ...
- ACM 字符串 题目整理
AC自动机 UVa 11468 Substring AC自动机+概率DP. 注意要补全不存在的边. 为什么要补全不存在的边呢?补全以后可以直接找到状态的转移,即从所有子节点就可以实现所有状态转移. ...
- ACM 暴力搜索题 题目整理
UVa 129 Krypton Factor 注意输出格式,比较坑爹. 每次要进行处理去掉容易的串,统计困难串的个数. #include<iostream> #include<vec ...
随机推荐
- Unity3D 5.1烘培 操作
http://blog.csdn.net/asd237241291/article/details/48056575 原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 Unity3D引擎技 ...
- 记一次基于Unity的Profiler性能分析
A. WaitForTargetFPS: Vsync(垂直同步)功能所,即显示当前帧的CPU等待时间 B. Overhead: Profiler总体时间-所有单项的记录时间总和.用于 ...
- win8style布局
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=" ...
- C/C++大数库简介
在网络安全技术领域中各种加密解密算法的软件实现上始终有一个共同的问题就是如何在普通的PC机上实现大数的运算.我们日常生活中所应用的PC机内部字长多是32位或64位,但是在各种加密解密的算法中为了达到一 ...
- 封装jquery时用到的东西
顺序都是瞎拍的,就是明显分割用 1.将函数封装成$(' ')这种形式 把函数名起成$ $(各种选择器) $(selector) 2.有时候jquery可以继续加点,返回自己本身的元素 创建个构造函数, ...
- jmeter 构建一个数据库测试计划
添加用户 第一步你想做的每一个JMeter测试计划是添加一个 线程组 元素. 的线程组 告诉JMeter的用户数量你想模拟,用户应该多长时间 发送请求,他们应该发送的请求的数量. 继续添加Thread ...
- Python 练习 11
#!/usr/bin/python # -*- coding: UTF-8 -*- import math for i in range(10000): #转化为整型值 x = int(math.sq ...
- 如何开发 Sublime Text 2 的插件
Sublime Text 2是一个高度可定制的文本编辑器,一直以来对希望有一个快速强大现代的编辑工具的的程序员保持着持续的吸引力.现在,我们将创建自己的一个Sublime plugin,实现用Nett ...
- css解决方案经验杂记
文本垂直居中 单行文本:line-height的值等于height: 多行文本:padding上下值一致即可: 还可以使用position:absolute进行绝对定位,如果是相对父级元素,则需要设置 ...
- 《Learning Play! Framework 2》学习笔记——案例研究1(Templating System)
注解: 这是对<Learning Play! Framework 2>第三章的学习 本章是一个显示聊天记录的项目,只有一个页面,可以自动对聊天记录进行排序.分组和显示,并整合使用了less ...