1 条题解

  • 0
    @ 2025-5-7 11:39:49

    动态规划+斜率优化+单调队列 注意边界的处理,由于STL的问题导致浪费了很多时间。 状态方程巧妙,逆序的方式很有效。

    方程的变化十分巧妙。 之前的方程: dp[i][j] 表示前i的工作分成j组的最小花费。 f[i]表示前i个工作的费用系数和。 t[i]表示前i个工作的时间总和。 dp[i][j] = min(dp[k][j-1]+(sj+t[i]-t[k])(f[i]-f[k]))

    改进的方程: f[i][j]中j这一维应该被消除。 考虑在顺序处理时,工作k1与工作k2(k1<k2),处理k2时的总时间包含了k1的时间,类似于每次做覆盖线段的操作,每次都要从头覆盖。 所以当从n开始逆序处理时,每次都只用处理当前段,而之后的费用自动累加。 巧妙的设计,值得研究。

    dp[i]表示倒数前i个工作的最小花费。 f[i]表示倒数前i个工作的费用系数和。 t[i]表示倒数前i个工作的时间总和。 dp[i] = min(dp[j]+(s+t[i]-t[j])*f[i])

    斜率优化: 对k1>k2,得到的f[i]分别为g[k1],g[k2]有 g[k1]-g[k2] = dp[k1]-dp[k2]-(t[k1]-t[k2])*f[i] 当g[k1]-g[k2]<0时,k1优于k2。 又由于f[i]为单调增加函数,所以当slope[k1,k2]=(dp[k1]-dp[k2])/(t[k1]-t[k2])>f[i]时,g[k1]始终优于g[k2]。

    所以有单调队列,保证slope[q1,q2]<slope[q2,q3]<slope[q3,q4]...,则每次最优解均为q1。 算法流程: 1.对于阶段i有f[i],首先从头扫描队列,如果slope[q[i],q[i+1]]<=f[i]则队头出队 (由于slope<=f[i]时q[i]一定不如q[i+1]更优),直到不满足为止。 这保证了队列中所有的slope[q[i],q[i+1]]>f[i] 2.队首出队,计算dp[i] = dp[q1]+(s+t[i]-t[q1])*f[i];

    • 3.i需要入队,从队尾开始扫描,如果slope[q[n-1],q[n]]>=slope[q[n],i],则队尾出队 (这一条满足了slope递增的性质),直到不满足为止。
    #include<cstdio>
    #include<algorithm>
    #include<deque>
    #define INF (1<<30)
    #define N 10005
    using namespace std;
    
    int t[10005],f[10005];
    long dp[10005];
    int que[N];
    long head,tail,sumq;
    int main()
    {
    	int n,s,i,k1,k2;
    	//    FILE* fin = fopen("d.in","r");
    	//    FILE* fout = fopen("d.out","w");
    	scanf("%d%d",&n,&s);
    	//    fscanf(fin,"%d%d",&n,&s);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d%d",&t[i],&f[i]);
    		//        fscanf(fin,"%d%d",&t[i],&f[i]);
    		dp[i] = INF;
    	}
    	for(i=n-1;i>=1;i--)
    		f[i] += f[i+1],t[i] += t[i+1];
    	
    	dp[n] = (s+t[n])*f[n];
    	head = sumq = 0,tail = -1;
    	que[++tail] = n,sumq++;
    	for(i=n-1;i>=1;i--)
    	{
    		while(sumq>1)
    		{
    			k1 = que[head];
    			k2 = que[(head+1+N)%N];
    			if((dp[k1]-dp[k2])-(t[k1]-t[k2])*f[i]>=0) 
    				head = (head+1+N)%N,sumq--;
    			else break;
    		}
    		
    		k1 = que[head];
    		dp[i] = (s+t[i])*f[i];
    		dp[i] = min(dp[i],dp[k1]+f[i]*(s+t[i]-t[k1]));
    		
    		while(sumq>1)
    		{
    			k1 = que[tail];
    			k2 = que[(tail-1+N)%N];
    			if((dp[i]-dp[k1])*(t[k1]-t[k2])<=(dp[k1]-dp[k2])*(t[i]-t[k1])) 
    				tail = (tail-1+N)%N,sumq--;
    			else break;
    		}
    		que[tail = (tail+1+N)%N] = i,sumq++;
    	}
    	
    	printf("%ld\n",dp[1]);
    	return 0;
    }
    • 1

    信息

    ID
    181
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者