POJ 1276 Time Machine【多重背包】

这个多重背包问题我单纯地把它转化成了0-1背包问题,然后列出状态转移方程 dp[i][j] = max{ dp[ i-1][ j - k * V[i]] + k , dp[ i-1][ j-1] }

这样看起来是需要三重循环哦。。开心的列了出来【图样图森破,简直拿衣服】

DP部分的代码写成了这样

for( i = 1; i <= n; i++){
			for( j = N; j >= bill[i].mon; j--){
				temp = 0;
				for( k = 1; k <= bill[i].fre; k++){
					if( j-k*bill[i].mon < 0) break;
					if( dp[j] < dp[j-k*bill[i].mon] + k)
						dp[j] = dp[j-k*bill[i].mon] + k;
				}
			}
		}
之后我猛然发现一个问题,对于给出的数据中取不到的值也会计算成使 dp[j] 最大的那个 j 值....比如对于输入数据 633 4  500 30  6 100  1 5  0 1 。应得数据应该为630,但是实际给出的还是633。。。

 我改了以后发现原来就算是630之前也会连续取一样的值。。。

那么如果记录最大值呢?然后我写成了这样。。。

for( i = 1; i <= n; i++){
			for( j = N; j >= bill[i].mon; j--){
				temp = 0;
				for( k = 1; k <= bill[i].fre; k++){
					if( j-k*bill[i].mon < 0) break;
					if( dp[j] < dp[j-k*bill[i].mon] + k)
						dp[j] = dp[j-k*bill[i].mon] + k;
					temp =  k*bill[i].mon;
				}
				if( temp > max) max = temp;
			}
		}
记录最大值这个思路是对的,但是我写错了,这个程序明显是求至票面额倍数的最大值。。。这里有点想当然了,没有进一步思考,下次要吸取教训

不过既然记录了最大值,而且也不需要求最大的张数,我们也就不需要记忆每一个钱数的张数了。那么只需要一个数组来记录某值是否被访问到就可以了。于是我就看懂了网上一部分人的题解23333333.。。。参考了别人的题解,我终于做出来了

下面放上AC题解之一【用dp来存储是否被访问到】

#include<stdio.h>
#include<string.h>
struct A{
	int mon;
	int fre;
}bill[15];
int main(void){
	int N, n, i, j, k, temp, max; 
	int dp[100005];
	while( scanf("%d", &N) != EOF){
		memset( dp, 0, sizeof(dp));
		scanf("%d", &n);
		for( i = 1; i <= n; i++){
			scanf("%d%d", &bill[i].fre, &bill[i].mon);
		}
		if( n == 0 || N == 0){
			printf("0\n"); continue;
		}
		max = 0;
		dp[0] = 1;
		for( i = 1; i <= n; i++){                                  // 对每一个bill进行遍历,之前我还弄反了
			for( j = max; j >= 0; j--){
				if( dp[j])
					for( k = 1; k <= bill[i].fre; k++){
						temp = j + k*bill[i].mon;  // 能够取到的值
						if( temp > N)continue;
						dp[temp] = 1;              // 记录,已经访问
						if( temp > max) max = temp;// 取最大值
					}
			}
		}
		printf("%d\n", max);
	}
	return 0;
}
事实上我之前是被之前自己写的转移方程固化了思维,没有想到这道题还可以这样想。

之前的状态转移方程应该是对的。。。。张数什么的都是能算出来的。。。要是换一个问法也许我就能AC了。。。。嘤嘤嘤(并不能,因为我还落后地使用着二维数组。。所以可能会超时T0T)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值