题意:有一台机器有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 ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
9183736 | 201030720425 | 1180 | Accepted | 808K | 79MS | G++ | 919B | 2011-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;
}