动态规划总结

基础dp

背包

背包九讲

  • 01背包
for (int i = 1; i <= n; i++)
    for (int j = V; j >= c[i]; j--)
        f[j] = max(f[j], f[j - c[i]] + w[i]);

初始化细节:
要求恰好装满背包:初始化时 f [ 0 ] f[0] f[0]为0, f [ 1... V ] = − ∞ f[1...V]=-\infty f[1...V]=
不要求把背包装满:初始化时 f [ 0... V ] = 0 f[0...V]=0 f[0...V]=0
体积为负的处理方法

  • 完全背包
for (int i = 1; i <= n; i++)
    for (int j = c[i]; j <= V; j++)
        f[j] = max(f[j], f[j - c[i]] + w[i]);

  • 多重背包
    假设第 i i i种物品有 s i s_i si个,那么把 s i s_i si拆成 1 , 2 , 4 , … , 2 k − 1 , n − 2 k + 1 1,2,4,…,2^{k-1},n-2^k+1 1,2,4,,2k1,n2k+1
int main() {
	cin>>N>>V;
	int cnt=0;
	for(int i=1; i<=N; i++) {
		int wi,vi,s;
		cin>>wi>>vi>>s;
		int k=1;		
		while(k<=s){
			cnt++;w[cnt]=wi*k;v[cnt]=vi*k;
			s-=k;k*=2;
		}
		if(s>0){
			cnt++;w[cnt]=wi*s;v[cnt]=vi*s;
		}
	}
	N=cnt;
	for(int i=1; i<=N; i++) {
		for(int j=V; j>=w[i]; j--)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[V]<<endl;
	return 0;
}
  • 混合背包
  • 二维费用背包
for (int i = 1; i <= n; i++)
    for (int j = V; j >= c[i]; j--)
        for (int k = M; k >= g[i]; k--)
            f[j][k] = max(f[j][k], f[j - c[i]][k - g[i]] + w[i]);

“二维费用”的条件多数是以这样一种隐含的方式给出的:最多只能取 M M M件物品。

  • 分组背包

f [ k ] [ j ] = m a x ( f [ k − 1 ] [ j ] , f [ k − 1 ] [ j − c [ i ] ] + w [ i ] ∣ 物 品 i ⊆ 组 k ) f[k][j]=max(f[k−1][j],f[k−1][j−c[i]]+w[i]∣物品i⊆组k) f[k][j]=max(f[k1][j],f[k1][jc[i]]+w[i]ik)

for (int i = 1; i <= n; i++) {
	cin >> s; // 物品组数
	for (int j = 1; j <= s; j++) cin >> c[j] >> w[j]; //组中每个物品的属性
    for (int j = V; j >= 0; j--)//这一层循环一定要在枚举组内元素的循环外面
        for (int k = 1; k <= s; k++)
            if (j >= c[k])
                f[j] = max(f[j], f[j - c[k]] + w[k]);
            // 由于每组物品只能选一个,所以可以覆盖之前组内物品最优解的来取最大值
}

  • 树上背包
  • 求第K优解
int kth(int n, int V, int k) {
    for (int i = 1; i <= n; i++) {
        for (int j = V; j >= w[i]; j--) {
            for (int l = 1; l <= k; l++) {
                a[l] = f[j][l];
                b[l] = f[j - w[i]][l] + v[i];
            }
            a[k + 1] = -1;
            b[k + 1] = -1;
            int x = 1, y = 1, o = 1;
            while (o != k + 1 and (a[x] != -1 or b[y] != -1)) {
                if (a[x] > b[y]) f[j][o] = a[x], x++;
                else f[j][o] = b[y], y++;
                if (f[j][o] != f[j][o - 1]) o++;
            }
        }
    }
    return f[V][k];
}


区间/环形dp

数位dp

图上dp

树形dp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值