01背包问题
基础代码:
#include<iostream>
using namespace std;
const int N = 1010 ;
int f[N][N];
int v[N] , w[N];
int main()
{
int n,m;
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++)
{
cin >> v[i] >> w[i];
}
for(int i = 1 ; i <= n ; i++)
{
for(int j = 0 ; j <= m ; j++)
{
f[i][j] = f[i - 1][j];
if(j >= v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]] + w[i]) ;
}
}
cout << f[n][m];
return 0;
}
一维优化代码(为什么转换成一维的时候,要从大到小)
for(int i = 1 ; i <= n ; i++)
{
for(int j = 0 ; j <= m ; j++)
{
f[i][j] = f[i - 1][j];
if(j >= v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]] + w[i]) ;
}
}
当单纯把 前面的i去掉而转换成一维的时候,上面的这行代码
if(j >= v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]] + w[i]) ;
就会变成
if(j >= v[i]) f[i][j] = max(f[i][j],f[i][j-v[i]] + w[i]) ;
// f[j] = max(f[j],f[j-v[i]] + w[i])
因为上面的j loop 是从小到大循环的,此时在i的循环中,先更新了 f[j-v[i]],所以这里是f[i][j-v[i],与之前公式不符,当j从大到小循环的时候,在i的循环中,f[j-v[i]]没有被先更新,所以是f[i-1][j-v[i]],所以这里应该用从大到小的顺序,而下面的完全背包,因为优化了结构,它的计算方程就是 f[i][j] = max(f[i][j],f[i][j-v[i]] + w[i]),所以它是从小到大的
一维代码:
#include<iostream>
using namespace std;
const int N = 1010 ;
int v[N] , w[N] , f[N];
int n , m ;
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++) cin >> v[i] >> w[i] ;
for(int i = 1 ; i <= n ; i++)
for(int j = m ; j >= v[i]; j--)
f[j] = max(f[j] , f[j-v[i]] + w[i]);
cout << f[m];
return 0;
}
完全背包问题
#include<iostream>
using namespace std;
const int N = 1010 ;
int n , m;
int v[N] , w[N] ;
int f[N][N];
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++)
{
cin >> v[i] >> w[i] ;
}
for(int i = 1 ; i <= n ; i++)
{
for(int j = 0 ; j <= m ; j++)
{
f[i][j] = f[i-1][j];
if(j >= v[i]) f[i][j] = max( f[i][j] , f[i][j-v[i]] + w[i]) ;
}
}
cout << f[n][m];
return 0 ;
}
优化过程
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2v]+2w , f[i-1,j-3v]+3w , …)
f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2v] + w , f[i-1,j-3v]+2*w , …)
由上两式,可得出如下递推关系:
f[i][j]=max(f[i,j-v]+w , f[i-1][j])
#include<iostream>
using namespace std;
const int N = 1010 ;
int v[N] , w[N] , f[N] ;
int n , m ;
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++) cin >> v[i] >> w[i] ;
for(int i = 1 ; i <= n ; i++)
{
for(int j = v[i] ; j <= m ; j++)
{
f[j] = max(f[j] , f[j-v[i]] + w[i]);
}
}
cout << f[m];
return 0 ;
}
多重背包问题I
#include<iostream>
using namespace std;
const int N = 110;
int f[N][N];
int a[N] , b[N] , c[N];
int n,v;
int main(){
cin >> n >> v ;
for(int i = 1 ; i <= n ; i++)
{
cin >> a[i] >> b[i] >> c[i];
}
for(int i = 1 ; i <= n ; i++)
{
for(int j = 0 ; j <= v ; j++)
{
f[i][j] = f[i-1][j];
for(int k = 0 ; k <= c[i] && k * a[i] <= j ; k++)
{
f[i][j] = max(f[i][j] , f[i-1][j-k*a[i]] + k * b[i]) ;
}
}
}
cout << f[n][v];
return 0 ;
}
一维优化:
#include<iostream>
using namespace std;
const int N = 110 ;
int v[N], w[N] , s[N];
int f[N];
int n , m ;
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++) cin >> v[i] >> w[i] >> s[i];
for(int i = 1; i<= n ; i++)
{
for(int j = m ; j >= v[i] ; j--)
{
for(int k = 1 ; k <= s[i] && k * v[i] <= j ; k++)
{
f[j] = max(f[j] , f[j-k*v[i]] + (k * w[i]));
}
}
}
cout << f[m] ;
return 0 ;
}
多重背包问题II
用二进制来理解,1+2+4+8+16+32+64 相当于 一个1xxxxxxx减去1的值,所以范围是0~64*2-1
#include<iostream>
using namespace std;
const int N = 24100 , M = 2010 ;
int f[M];
int n , m ;
int v[N],w[N],s[N];
int main()
{
cin >> n >> m ;
int cnt = 0 ;
for(int i = 1 ; i <= n ; i++)
{
int a,b,s ;
cin >> a >> b >> s;
int k = 1;
while(k <= s)
{
cnt++ ;
v[cnt] = a * k ;
w[cnt] = b * k ;
s -= k ;
k *= 2 ;
}
if(s > 0)
{
cnt ++ ;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt ;
for(int i = 1 ; i <= n ; i++)
{
for(int j = m ; j >= v[i] ; j--)
{
f[j] = max(f[j] , f[j-v[i]] + w[i]);
}
}
cout << f[m];
return 0;
}
分组背包
#include<iostream>
using namespace std;
const int N = 110 ;
int v[N][N] , w[N][N] ;
int t[N];
int f[N];
int n ,m ;
int main()
{
cin >> n >> m ;
for(int i = 1; i <= n ; i++)
{
cin >> t[i];
for(int j = 1 ; j <= t[i] ; j++)
{
cin >> v[i][j] >> w[i][j];
}
}
for(int i = 1 ; i <= n ; i++)
{
for(int j = m; j >= 0 ; j--)
{
for(int k = 1 ; k <= t[i] ; k++)
{
if(j >= v[i][k]) f[j] = max(f[j] ,f[j-v[i][k]] + w[i][k]) ;
}
}
}
cout << f[m];
return 0 ;
}