动态规划代码总整理
\(1.\) 线性dp
最长单调递增子序列
signed main(){
int n,a[105],m=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int dp[105]={0};
dp[1]=1;
for(int i=2;i<=n;i++){
int k=0;
for(int j=1;j<i;j++){
if(a[i]>a[j]&&dp[j]>k){
k=dp[j];
}
}
dp[i]=k+1;
if(m<dp[i]){
m=dp[i];
}
}
printf("%d",m);
return 0;
}
\(2.\) 背包dp
(1) 部分背包
struct Goods{
int w;
int v;
double p;
}g[1005];
bool cmp(struct Goods g1,struct Goods g2) return g1.p<g2.p;
signed main() {
int i,n,C;
double sum,val=0;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d %d",&g[i].w,&g[i].v);
g[i].p=(g[i].w*1.0)/g[i].v;
}
scanf("%d",&C);
sort(g,g+n,cmp);
for(i=0;i<n;i++) {
if(sum+g[i].w<C){
sum+=g[i].w;
val+=g[i].v;
}
else break;
}
val+=((C-sum)*g[i].p);
printf("%.2lf\n",val);
return 0;
}
(2) 01背包
signed main(){
int n,w[105],c[105],dp[105][105]={0},v;
scanf("%d%d",&n,&v);
for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++){
for(int j=v;j>=0;j--){
if(w[i]<=j){
dp[j]=max();
}
}
}
printf("%d",dp[v]);
return 0;
}
(3) 完全背包
long long dp[30][10005]={0};
signed main(){
int v,n,a[30];
scanf("%d%d",&v,&n);
for(int i=1;i<=v;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
if(a[1]<=i&&i%a[1]==0) dp[1][i]=1;
if(a[1]==1)
for(int i=1;i<=v;i++)
dp[i][1]=1;
for(int i=2;i<=v;i++) dp[i][0]=1;
for(int i=2;i<=v;i++){
for(int j=1;j<=n;j++){
if(j<a[i]) dp[i][j]=dp[i-1][j];
else dp[i][j]=dp[i-1][j]+dp[i][j-a[i]];
}
}
printf("%lld",dp[v][n]);
return 0;
}
\(3.\) 区间dp
\(n\) 堆石子,第 \(i\) 堆有 \(a_{i}\) 个,合并时合并相邻两堆,产生的代价为两堆果子的总数,求合并成一堆后的最小代价。
signed main(){
int n,a[105],sum[105]={0};
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
int dp[105][105]={0};
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
dp[i][j]=0x3f3f3f3f;
for(int k=i;k<j;k++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
}
printf("%d",dp[1][n]);
}
\(4.\) 数位dp
给出一个数 \(n\),\(1 \sim n\) 有多少数包含 \(49\)。
数据:\(1 \leq T \leq 10000\),\(1 \leq a_i \leq 2^{63}-1\)
const int Max = 99999;
const int Min = 0;
const int inf = 1e6;
const int mod = 1e9+7;
#define M 1000
#define N 1000
#define ll long long
#define swap(x,y) x^=y^=x^=y
ll dp[20][6];
int digit[20];
ll dfs(int pos,int pre,int sta,bool limit){
if(pos==-1) return 1;
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int up = limit?digit[pos]:9;
ll sum(0);
for(int i=0;i<=up;i++){
if(pre==4&&i==9) continue;
sum += dfs(pos-1,i,i==4,limit&&i==digit[pos]);
}
if(!limit) dp[pos][sta] = sum;
return sum;
}
ll solve(ll a){
int cnt = 0;
while(a>0){
digit[cnt++] = a%10;
a/=10;
}
ll ans = dfs(cnt-1,0,0,true);
return ans;
}
signed main(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
int T;
cin >> T;
memset(dp,-1,sizeof(dp));
while(T--){
ll a;
scanf("%I64d",&a);
ll ans = solve(a);
cout << a+1-ans << endl;
}
return 0;
}
\(5.\) 树形dp
(1) 树上最长链
const int N = 1e5 + 10;
int n, root;
vector<int>e[N];
int w[N], du[N];
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int dfs(int u, int ans, int val) {
int res = 0; res = max(res, ans);
for (auto& v : e[u]) {
int x = gcd(val, w[v]);
if (x == 1)continue;
else res = max(res, dfs(v, ans + 1, x));
}
return res;
}
signed main() {
cin >> n;
for (int i = 1; i < n; ++i) {
int a, b; cin >> a >> b;
e[a].push_back(b); du[b]++;
}
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= n; ++i)
if (!du[i]) {
root = i;
break;
}
cout << dfs(root, 1, w[root]) << endl;
return 0;
}
(2) 树的直径
const int N=2e4+10;
int e[N],ne[N],w[N],h[N],idx;
int f1[N],f2[N];
void add(int a,int b,int c){
e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void dfs(int u,int fa){
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;
dfs(j,u);
int d=f1[j]+w[i];
if(d>f1[u]){
f2[u]=f1[u];
f1[u]=d;
}
else if(d>f2[u]){
f2[u]=d;
}
}
}
signed main(){
int n;
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n-1;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dfs(1,-1);
int ans=-1e9;
for(int i=1;i<=n;i++){
ans=max(ans,f1[i]+f2[i]);
}
cout<<ans<<endl;
return 0;
}
\(6.\) 状压dp
旅行家在 \(n\) 个点 \(m\) 条边的有向图中,从一个点出发,各个城市经历一次且仅一次,然后回到出发城市,求路径最短。
#define M 19
#define INF 0x3f3f3f3f
int n;
int mp[M][M];
int dp[1<<11][M];{
if(dp[s][v] >= 0) return dp[s][v];
if(s == (1<<n)-1 && v == 0) return dp[s][v] = 0;
int ret = INF;
for(int u = 0;u < n;u++){
if(!((s >> u) & 1))
ret = min(ret,slove(s|(1<<u),u)+mp[v][u]);
}
return dp[s][v] = ret;
}
int main(){
while(scanf("%d",&n) == 1 && n){
for(int i = 0;i < n;i++) fill(mp[i],mp[i]+n,INF);
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++)
scanf("%d",&mp[i][j]);
}
memset(dp,-1,sizeof(dp));
printf("%d\n",slove(0,0));
}
return 0;
}

浙公网安备 33010602011771号