Regionals 2009 Asia - Hsinchu uvalive 4526 Inventory - dp

题目链接:

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=350&page=show_problem&problem=2527

题目大意:

有个工厂要生产一些产品,生产之前先要买一些原材料,但是每天材料的价格不一样,今天买的材料只能今天用或者明天用,每天必须完成每天的任务不能留给下一天,现在要求完成n天的任务的最小花费。

思路:dp

刚开始一直用贪心,wa的简直不成人样。。。现在依然还是觉得贪心可以用,只是大概我的姿势不对。。

dp[i][j] 表示第i天完成任务后还剩余j个材料最小花费,剩余的j个可以留给下一天用。

那么枚举天数i,枚举留给下一天的个数j,再枚举从前一天得到k,

dp[i][j] = min(dp[i][j],dp[i-1][k] + p[i]*(h[i]+j-k));

p[i],表示第i天的价格

h[i],表示第i天需要完成产品数


直接三重循环,tle。。

想一想,主要还是w[i],也就是第i天最多能买的材料数太大,原来是j<=w[i],也就是只考虑传给下一天只受这一天能买的最多的数限制,但其实还有一个条件,就是后一天最多只能接受h[i+1]个,多了就浪费了,所以j<=min(w[i],h[i+1]),这样j的范围就可能会小了很多。


其实一开始准备交的时候,我还是很担忧的,毕竟是1000*100*100按道理是会超时的,但还是过了。。

所以,dp还是要大胆的想,大胆的写,只要状态确定有道理。递推式没问题那就没啥好担心的了。。


代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define maxn 1010
#define inf 0x3fffffff

int p[maxn],w[maxn],h[maxn];
int dp[maxn][110];

int main(){
	int T,n;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d%d%d",&p[i],&w[i],&h[i]);
		}
		memset(dp,0x3f,sizeof(dp));
		// dp[i][j] 表示第i天完成任务后还剩余j个材料最小花费 
		dp[1][0] = p[1]*h[1];
		for(int i=1;i<=w[1]-h[1];i++) {
			dp[1][i] = dp[1][i-1] + p[1];
			//printf("i:%d j:%d %d\n",1,i,dp[1][i]);
		}
		
		for(int i=2;i<=n;i++){
			int t = i==n?0:min(w[i],h[i+1]);
			for(int j=0;j<=t;j++){
				for(int k=0;k<=h[i];k++){
					if(h[i] + j-k <=w[i] && dp[i-1][k]!=inf){
						dp[i][j] = min(dp[i][j],dp[i-1][k] + p[i]*(h[i]+j-k));
					}
				}
				//printf("i:%d j:%d %d\n",i,j,dp[i][j]);
			}
		}
		printf("%d\n",dp[n][0]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值