poj 1180

题意:有一台机器有n个任务,可以将这些任务分组进行,分组条件是只能按顺序一段段分,不能有交叉,而分组后每组在进行加工的时候都有一个相同的准备时间s,每个任务完成时间按整组的完成时间算,每个任务还有一个消费,消费是按你完成任务的时间*该任务的消费单位f。求最小消费。

对于这种分组求最优值问题一般都是dp问题,然而这题的状态不是那么好设定的,二维的状态很容易想到,数组太大,可以优化,但复杂度极高,用四边形优化也是很高,除非你卡卡数据,否则很难过,不过好像卡卡能过,这题的状态我实在想不出,写了一个一维状态有个记录时间,结果化简不了斜率,看人家的题解才知道是反过来设状态。dp[i]表示加入任务i后到任务n的最小消费。这样设定的好处在于你每次增加一个分组,就会使得后面的时间后移,只要加上这个增量即可,看起来麻烦,但是可以将你的式子化到很简单,dp[i]=dp[k]+(s+sumt[i]-sumt[k])*(sumf[i]-sumf[k])+(s+sumt[i]-sumt[k])*sumf[k];化简后就是dp[i]=dp[k]+(s+sumt[i]-sumt[k])*sumf[i];然后求出G函数和S函数和斜率就好了,很简单。主要是这个状态难想,还有就是这个转移方程,刚开始好难理解,后面拿笔算算就明白了。

这题按我一贯的写法写完后交果断wa,wa了近20多次后,看人家的代码终于明白了一个东西,斜率的优化,要考虑全面,因为这题完全可以全部为1组做完,那就是还有一个sumt[n+1]=0,的这个东西加进去,因为反过来的所以是n+1.不做题很难发现这些自己平时不注意的东东。


Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time
91837362010307204251180Accepted808K79MSG++919B2011-08-17 21:00:58
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
long long dp[10005];
int sumt[10005],sumf[10005];
long long G(int x,int y)
{
	return dp[x]-dp[y];
}
long long S(int x,int y)
{
	return sumt[x]-sumt[y];
}
int q[10005],r,f;
void init(){r=f=0;}
int main()
{
	int n,m;
	scanf("%d",&n);
	scanf("%d",&m);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&sumt[i],&sumf[i]);
	for(int i=n-1;i>0;i--)
	{
		sumt[i]+=sumt[i+1];
		sumf[i]+=sumf[i+1];
	}
	init();
	q[++r]=n+1;
	for(int i=n;i>0;i--)
	{
		while(f+1<r&&G(q[f+2],q[f+1])<=sumf[i]*S(q[f+2],q[f+1]))
			f++;
		int tem=q[f+1];
		dp[i]=dp[tem]+(m+sumt[i]-sumt[tem])*sumf[i];
		q[++r]=i;
		for(int j=r-1;j-1>f;j--)
		{
			int x=q[j-1],y=q[j],z=q[j+1];
			if(!(G(y,x)*S(z,y)<G(z,y)*S(y,x)))
				q[j]=q[r--];
			else
				break;
		}
	}
	printf("%lld\n",dp[1]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值