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 ...
随机推荐
- APMServ本地搭建网站最好用的软件
APMServ 5.2.6 是一款拥有图形界面的快速搭建Apache 2.2.9.PHP 5.2.6.MySQL 5.1.28&4.0.26.Nginx 0.7.19.Memcached 1. ...
- 如何在腾讯云快速构建一个Wordpress个人站点
版权声明:本文由贺嘉原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/175 来源:腾云阁 https://www.qclou ...
- 利用JAXB实现java实体类和xml互相转换
1.应用场景 在使用WebService实现数据上传下载,数据查询时,可以利用JAXB实现java实体类和xml互相转换 2.Demo 2.1 student.java 实体类,包含list(set同 ...
- Azure平台 对Twitter 推文关键字进行实时大数据分析
Learn how to do real-time sentiment analysis of big data using HBase in an HDInsight (Hadoop) cluste ...
- mysql 各种运算对于null值的处理
1.A,B,C作为列名时,任意一项为null 则A+B+C为null; 2.count对于null值处理; count(*)包含null项:count(具体列名)忽略null项;count(null) ...
- Shell基础:常用技巧&重定向&管道操作
Shell脚本介绍和常用工具 Shell脚本 Shell脚本:实际就是windows里的批处理脚本,多条可一次执行的Shell命令集合.Linux上的脚本可以用很多种语言实现,bash shell是比 ...
- python中range和xrange的区别
1.range生成一个列表:xrange生成一个生成器 2.用法都差不多
- 布隆过滤器(Bloom Filter)详解——基于多hash的概率查找思想
转自:http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html 布隆过滤器[1](Bloom Filter)是由布隆(Burton ...
- BZOJ3942 [Usaco2015 Feb]Censoring
维护一个栈...如果栈顶出现了要被删除的字符串就全删掉就好了,判断的话...kmp就行了 /****************************************************** ...
- 如何在win7下配置IIS?