Hdu 1114 Piggy-Bank

大意不再赘述。

思路:虽然说是完全背包,但感觉和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;
}


你可能感兴趣的:(Hdu 1114 Piggy-Bank)