1、01背包
1. 基本01背包(压维度)
dp[i]:前i个物品,体积为j的最大价值
for ( int i = 1 ; i <= m; i++ )
{
for ( int j = t; j >= a[ i] . time; j-- )
{
dp[ j] = max ( dp[ j - a[ i] . time] + a[ i] . val, dp[ j] ) ;
}
}
dp[j]:前i个数,体积为j的种类数 初始状态:
d
p
[
0
]
=
1
dp[0] = 1
d p [ 0 ] = 1 转移方程:
d
p
[
i
]
[
j
]
+
=
d
p
[
i
−
1
]
[
j
−
x
]
)
dp[i][j] += dp[i-1][j-x])
d p [ i ] [ j ] + = d p [ i − 1 ] [ j − x ] ) ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const int N = 1e4 + 8 ;
int dp[ N] ;
int main ( )
{
IOS;
int n, m;
cin >> n >> m;
dp[ 0 ] = 1 ;
rep ( i, 1 , n)
{
int x;
cin >> x;
for ( int j = m; j >= x; j-- )
{
dp[ j] += dp[ j - x] ;
}
}
cout << dp[ m] ;
return 0 ;
}
2、完全背包
问题描述:给你n个数a[i],a[i]可以使用任意次,现问你这个n个数有哪些只能被唯一表示。 完全背包记录方案,当dp[i] = 1时,代表值为i的数,只能由一个数组成,及ans++。 ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const int N = 25010 ;
int dp[ N] ;
int n;
int t;
int a[ 105 ] ;
int maxx = 0 ;
int main ( )
{
IOS;
cin >> t;
while ( t-- )
{
int n;
cin >> n;
rep ( i, 1 , n) cin >> a[ i] , maxx = max ( maxx, a[ i] ) ;
memset ( dp, 0 , sizeof ( dp) ) ;
dp[ 0 ] = 1 ;
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = a[ i] ; j <= maxx; j++ )
{
dp[ j] += dp[ j - a[ i] ] ;
}
}
int ans = 0 ;
for ( int i = 1 ; i <= n; i++ )
{
if ( dp[ a[ i] ] == 1 ) ++ ans;
}
cout << ans << '\n' ;
}
return 0 ;
}
3、多重背包
多重背包二进制优化后,转换为01背包 ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
vector< pair< int , int >> ve;
int dp[ 2004 ] ;
int main ( )
{
IOS;
int n, v;
cin >> n >> v;
rep ( i, 1 , n)
{
int av, aw, as;
cin >> av >> aw >> as;
int cnt = 1 ;
while ( as >= cnt)
{
ve. push_back ( { av * cnt, aw * cnt} ) ;
as -= cnt;
cnt <<= 1 ;
}
if ( as > 0 )
ve. push_back ( { av * as, aw * as} ) ;
}
int len = ve. size ( ) ;
for ( int i = 0 ; i < len; i++ )
{
for ( int j = v; j >= ve[ i] . first; j-- )
{
dp[ j] = max ( dp[ j] , dp[ j - ve[ i] . first] + ve[ i] . second) ;
}
}
cout << dp[ v] ;
return 0 ;
}
dp[i][j]:选择前i个物品,体积为j的最大价值 三重循环:一维是枚举物品,二维是枚举价值,三维是枚举决策(选择该物品的数量) ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const double PI = acos ( - 1.0 ) ;
struct node
{
int v, w, s;
} a[ 505 ] ;
int dp[ 6002 ] ;
int main ( )
{
IOS;
int n, m;
cin >> n >> m;
for ( int i = 1 ; i <= n; i++ )
cin >> a[ i] . v >> a[ i] . w >> a[ i] . s;
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = m; j >= 0 ; j-- )
{
for ( int k = 0 ; k <= a[ i] . s && k * a[ i] . v <= j; k++ )
{
dp[ j] = max ( dp[ j] , dp[ j - a[ i] . v * k] + a[ i] . w * k) ;
}
}
}
cout << dp[ m] ;
return 0 ;
}
4、二维费用背包
询问体积至少是j的最小花费。 费用背包类型:
体积最多为j:dp[i][j],表示前i个物品,体积至多为j,初始值全为0,v >= 0 体积恰好为j:dp[i][j],表示前i个物品,体积恰好为j,dp[0][0] = 0,其余全是inf,v >= 0 体积至少为j:dp[i][j],表示前i个物品,体积至少为j,dp[0][0] = 0,其余全是inf,v 可以小于0
转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
m
a
x
(
j
−
a
,
0
)
]
+
w
)
dp[i][j] = max(dp[i][j],dp[i][max(j-a,0)]+w)
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i ] [ m a x ( j − a , 0 ) ] + w ) ,体积至少为
m
a
x
(
j
−
a
,
0
)
]
max(j-a,0)]
m a x ( j − a , 0 ) ] ,装上物品a,则是体积至少为
j
j
j ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const int N = 1e4 + 4 ;
int n, m, k;
int dp[ 505 ] [ 505 ] ;
int main ( )
{
IOS;
cin >> n >> m >> k;
memset ( dp, 127 , sizeof ( dp) ) ;
dp[ 0 ] [ 0 ] = 0 ;
for ( int i = 1 ; i <= k; i++ )
{
int a, b, c;
cin >> a >> b >> c;
for ( int j = n; j >= 0 ; j-- )
{
for ( int z = m; z >= 0 ; z-- )
{
dp[ j] [ z] = min ( dp[ j] [ z] , dp[ max ( j - a, 0 ) ] [ max ( z - b, 0 ) ] + c) ;
}
}
}
cout << dp[ n] [ m] ;
return 0 ;
}
3、分组背包
dp[i][j]:前i组物品,j容量的最大价值 第一维枚举组,第二维枚举当前组的各个物品,第三维枚举体积 ACcode
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const int N = 105 ;
int n, v;
int main ( )
{
IOS;
int dp[ N] ;
memset ( dp, 0 , sizeof ( dp) ) ;
cin >> n >> v;
int av[ N] , aw[ N] ;
for ( int i = 1 ; i <= n; i++ )
{
int s;
cin >> s;
for ( int k = 1 ; k <= s; k++ )
cin >> av[ k] >> aw[ k] ;
for ( int j = v; j >= 1 ; j-- )
{
for ( int k = 1 ; k <= s; k++ )
{
if ( j >= av[ k] )
dp[ j] = max ( dp[ j] , dp[ j - av[ k] ] + aw[ k] ) ;
}
}
}
cout << dp[ v] ;
return 0 ;
}
分组背包求方案数,不能压缩状态,求方案时,从dp[n][m]倒序枚举。 ACcode
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
int n, m;
int dp[ 15 ] [ 30 ] ;
int a[ 15 ] [ 30 ] ;
int ans[ 15 ] ;
int main ( )
{
IOS;
cin >> n >> m;
rep ( i, 1 , n)
rep ( j, 1 , m)
cin >> a[ i] [ j] ;
for ( int i = 1 ; i <= n; i++ )
{
for ( int j = 1 ; j <= m; j++ )
{
dp[ i] [ j] = dp[ i - 1 ] [ j] ;
for ( int k = 1 ; k <= m; k++ )
{
if ( j >= k)
dp[ i] [ j] = max ( dp[ i] [ j] , dp[ i - 1 ] [ j - k] + a[ i] [ k] ) ;
}
}
}
cout << dp[ n] [ m] << "\n" ;
int v = m;
for ( int i = n; i; i-- )
{
for ( int k = 1 ; k <= m; k++ )
{
if ( v >= k && dp[ i] [ v] == dp[ i - 1 ] [ v - k] + a[ i] [ k] )
{
ans[ i] = k;
v -= k;
break ;
}
}
}
rep ( i, 1 , n) cout << i << " " << ans[ i] << "\n" ;
return 0 ;
}
4、有依赖的背包
树形DP套01背包,将u号节点的儿子看做物品,儿子的属性看做体积,儿子的dp则为价值。 ACcode:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const int N = 104 ;
struct node
{
int v, w;
} a[ N] ;
vector< int > e[ N] ;
int n, av;
int dp[ N] [ N] ;
void dfs ( int u, int fa)
{
for ( auto v : e[ u] )
{
if ( v == fa)
continue ;
dfs ( v, u) ;
for ( int j = av - a[ u] . v; j >= 0 ; j-- )
{
for ( int k = 0 ; k <= j; k++ )
dp[ u] [ j] = max ( dp[ u] [ j] , dp[ u] [ j - k] + dp[ v] [ k] ) ;
}
}
for ( int i = av; i >= 0 ; i-- )
{
if ( i >= a[ u] . v)
dp[ u] [ i] = dp[ u] [ i - a[ u] . v] + a[ u] . w;
else
dp[ u] [ i] = 0 ;
}
}
int main ( )
{
IOS;
cin >> n >> av;
int rt = 0 ;
rep ( i, 1 , n)
{
int p;
cin >> a[ i] . v >> a[ i] . w >> p;
if ( p == - 1 )
rt = i;
else
{
e[ p] . push_back ( i) ;
e[ i] . push_back ( p) ;
}
}
dfs ( rt, 0 ) ;
cout << dp[ rt] [ av] ;
return 0 ;
}
5、背包求方案数
在01背包的基础上,求解最优解法的方案数。 在传统01背包的基础上,我们在设一个dp[i][j],表示前i个物品中,体积恰好为j时,最优解的方案数量。 ACcode
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const double PI = acos ( - 1.0 ) ;
const ll mod = 1e9 + 7 ;
int dp[ 1002 ] , dp2[ 1002 ] ;
int main ( )
{
IOS;
int n, v;
cin >> n >> v;
dp2[ 0 ] = 1 ;
for ( int i = 1 ; i <= n ; i++ )
{
int av, aw;
cin >> av >> aw;
for ( int j = v; j >= av; j-- )
{
int maxx = max ( dp[ j] , dp[ j - av] + aw) , cnt = 0 ;
if ( maxx == dp[ j] )
cnt += dp2[ j] , cnt %= mod;
if ( maxx == dp[ j - av] + aw)
cnt += dp2[ j - av] , cnt %= mod;
dp2[ j] = cnt;
dp[ j] = maxx;
}
}
int ans = 0 ;
for ( int i = 0 ; i <= v; i++ ) if ( dp[ i] == dp[ v] ) ans += dp2[ i] , ans %= mod;
cout << ans;
return 0 ;
}
归结到图论中进行。DP其实出来就是一张DAG图 ACcode
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define rep ( i, a, n) for ( int i = a; i <= n; i++ )
# define per ( i, a, n) for ( int i = n; i >= a; i-- )
# define lowbit ( x) ( ( x) & - ( x) )
# define lson p << 1 , l, mid
# define rson p << 1 | 1 , mid + 1 , r
# define mem ( a, b) memset ( a, b, sizeof ( a) )
# define IOS \
ios:: sync_with_stdio ( false ) ; \
cin. tie ( 0 ) ; \
cout. tie ( 0 )
const double PI = acos ( - 1.0 ) ;
const ll mod = 1e9 + 7 ;
const int N = 1004 ;
int dp[ N] [ N] ;
struct node
{
int v, w;
} a[ N] ;
int n, m;
int main ( )
{
IOS;
cin >> n >> m;
rep ( i, 1 , n)
cin >> a[ i] . v >> a[ i] . w;
for ( int i = n; i >= 1 ; i-- )
for ( int j = 0 ; j <= m; j++ )
{
if ( j >= a[ i] . v)
dp[ i] [ j] = max ( dp[ i + 1 ] [ j] , dp[ i + 1 ] [ j - a[ i] . v] + a[ i] . w) ;
else
dp[ i] [ j] = dp[ i + 1 ] [ j] ;
}
int j = m;
for ( int i = 1 ; i <= n; i++ )
{
if ( j >= a[ i] . v && dp[ i] [ j] == dp[ i + 1 ] [ j - a[ i] . v] + a[ i] . w)
{
cout << i << " " ;
j -= a[ i] . v;
}
}
return 0 ;
}