大意不再赘述。
思路:虽然说是完全背包,但感觉和DAG上固定终点的最短路差不多,只不过“面值只和恰为S”改成了“体积之和不超过C”,另外增加了一个新的属性---重量,把原来的无权图变成了带权图。这样,问题就变成了以C为起点,终点为0的、边权之和最小的路径,同样可以用动归递推的方式求解。
而且我们可以选择是否打印路径(字典序),唔,与0/1背包的联系与区别,个人感觉0/1背包选用滚动数组优化的话,路径打印比较困难,即使使用二维数组写似乎也比较困难。
另外,这里递推的顺序是可以改变的。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int MAXN = 10100;
const int INF = 0x3f3f3f3f;
int V[MAXN], W[MAXN];
int d[MAXN];
int n, S;
int first;
void init()
{
first = 1;
d[0] = 0;
for(int i = 1; i <= S; i++) d[i] = INF;
}
void dp()
{
init();
for(int i = 1; i <= n; i++)
{
for(int j = V[i]; j <= S; j++)
{
d[j] = min(d[j], d[j-V[i]]+W[i]);
}
}
}
/*void dp()
{
init();
for(int i = 1; i <= S; i++)
{
for(int j = 1; j <= n; j++) if(i >= V[j])
{
d[i] = min(d[i], d[i-V[j]]+W[j]);
}
}
}*/
void print_ans(int *d, int S) //打印的是路径上的边,而不是点
{
for(int i = 1; i <= n; i++) if(S >= V[i] && d[S] == d[S-V[i]]+W[i])
{
if(first)
{
printf("%d", i);
first = 0;
}
else printf(" %d", i);
print_ans(d, S-V[i]);
break;
}
}
/*void print_ans(int i) //打印路径上的点,如嵌套矩形
{
if(first)
{
printf("%d", i);
first = 0;
}
else printf(" %d", i);
for(int j = 1; j <= n; j++) if(G[i][j] && d[i] == d[j]+1)
{
print_ans(j);
break;
}
}*/
void read_case()
{
int bW, eW;
scanf("%d%d", &bW, &eW);
S = eW-bW;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d%d", &W[i], &V[i]);
}
void solve()
{
read_case();
dp();
if(d[S] != INF)
{
printf("The minimum amount of money in the piggy-bank is %d.\n", d[S]);
//printf("The Path is:\n");
//print_ans(d, S);
//printf("\n");
}
else printf("This is impossible.\n");
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
solve();
}
return 0;
}