Problem
codeforces.com/problemset/problem/730/J
题意
n 瓶水,每瓶水量 ai,容量 bi。要将所有水装到尽量少的瓶子内。
每移动一单位的水要消耗一单位时间,在最少瓶子的前提下,问移动水所需的最短时间。
Analysis
所需最少瓶子数可以贪心地求出,优先装容量大的瓶子。
DP。最初想的状态是:dp[i][j][k]:前 i 个瓶子里挑 j 个,总水量是 k 时的最短移动时间。然后找不到转移方程。
换状态:因为移一个单位水消耗一单位时间,知道需要 k 个瓶子后,时间就是:“总水量 - 这k个瓶子原有的水量和”,那么就可以找 k 个总容量够装所有水,且已有水量总和最大的瓶子。
定义状态:dp[i][j][k]:前 i 个瓶子挑 j 个,它们总容量为 k 时最大的原有水量和。
转移方程:dp[i][j][k] = max { dp[i-1][j][k],dp[i-1][j-1][k-b[i]] + a[i] }
跑完 dp 在 dp[瓶子数][最少瓶子数][0 ~ 瓶子总容量] 中找最大值,再用总水量减掉此最大值,就是最小时间。
或者可以反过来定义:dp[i][j][k]:前 i 个瓶子挑 j 个,这 已有水量和为 k 时的最大瓶子容量和。
状态转移:dp[i][j][k] = max{ dp[i-1][j][k],dp[i-1][j-1][k-a[i]] + b[i] }
然后在 dp[瓶子数][最少瓶子数][0 ~ 总水量] 找答案。
实现时省去第一维,里面两层循环都改成逆序。
初始化为 -1,表示此状态不可达到,也就不能用这个状态转移出其它状态。
Source code
第一种状态定义
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100, V = 100;
struct bottle
{
int a, b;
bool operator < (const bottle &bt) const
{
return b > bt.b;
}
} bot[N+1];
int dp[N+1][N*V+1];
int main()
{
int n, shui = 0, rong = 0;
scanf("%d", &n);
for(int i=1; i<=n; ++i)
{
scanf("%d", &bot[i].a);
shui += bot[i].a;
}
for(int j=1; j<=n; ++j)
{
scanf("%d", &bot[j].b);
rong += bot[j].b;
}
sort(bot + 1, bot + n + 1);
int ping = 0;
for(int i=1, w=shui; w>0; ++i)
{
w -= bot[i].b;
++ping;
}
memset(dp, -1, sizeof dp);
dp[0][0] = 0;
for(int i=1; i<=n; ++i)
for(int j=ping; j; --j)
for(int k=rong; k>=bot[i].b; --k)
if(~dp[j-1][k-bot[i].b]) // 先判是否合法状态
dp[j][k] = max(dp[j][k], dp[j-1][k-bot[i].b] + bot[i].a);
int mx = 0;
for(int i=shui; i<=rong; ++i)
mx = max(mx, dp[ping][i]);
printf("%d %d\n", ping, shui-mx);
return 0;
}
第二种状态定义
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100, V = 100;
struct bottle
{
int a, b;
bool operator < (const bottle &bt) const
{
return b > bt.b;
}
} bot[N+1];
int dp[N+1][N*V+1];
int main()
{
int n, shui = 0;
scanf("%d", &n);
for(int i=1; i<=n; ++i)
{
scanf("%d", &bot[i].a);
shui += bot[i].a;
}
for(int j=1; j<=n; ++j)
scanf("%d", &bot[j].b);
sort(bot + 1, bot + n + 1);
int ping = 0;
for(int i=1, w=shui; w>0; ++i)
{
w -= bot[i].b;
++ping;
}
memset(dp, -1, sizeof dp);
dp[0][0] = 0;
for(int i=1; i<=n; ++i)
for(int j=ping; j; --j)
for(int k=shui; k>=bot[i].a; --k)
if(~dp[j-1][k-bot[i].a])
dp[j][k] = max(dp[j][k], dp[j-1][k-bot[i].a] + bot[i].b);
int mx = 0;
for(int i=shui; ~i; --i)
if(dp[ping][i] >= shui)
{
mx = i;
break;
}
printf("%d %d\n", ping, shui-mx);
return 0;
}