求助,斜率优化最后一个点WA
  • 板块P2365 任务安排
  • 楼主__gcd
  • 当前回复2
  • 已保存回复2
  • 发布时间2020/8/30 15:25
  • 上次更新2023/11/5 13:57:34
查看原帖
求助,斜率优化最后一个点WA
149192
__gcd楼主2020/8/30 15:25

思路:倒序进行DP,dp(i)dp(i) 为完成 ii 后任务的最小花费(从 ii 开始做),令 cost=sumt(j1)sumt(i1)+scost=sumt(j-1)-sumt(i-1)+sij1i\sim j-1 所用时间) 可以得到状态转移方程:

dp(i)=mindp(j)+cost(sumf[j1]sumf[i1])+(sumf(n)sumf(j1))(j(i,n])dp(i)=\min dp(j)+cost\cdot(sumf[j-1]-sumf[i-1])+(sumf(n)-sumf(j-1))(j\in (i,n])

其中 sumtsumtsumfsumf 分别是 ttff 的前缀和数组。

化简一下可以得到:

dp(i)=mincost(sumf(n)sumf(i1))dp(i)=\min cost\cdot(sumf(n)-sumf(i-1))

假设 j<kj<kjj 是比 kk 更优的决策点,推推式子就可以得到:

sumf(i1)dp(j)+sumt(j1)sumf(n)dp(k)+sumt(k1)sumf(n))sumt(j1)sumt(k1)sumf(i-1)\leq \dfrac{dp(j)+sumt(j-1)\cdot sumf(n)-dp(k)+sumt(k-1)\cdot sumf(n))}{sumt(j-1)-sumt(k-1)}

所以用单调队列维护,保证下标递减,斜率递减。

WA on test 10,不保证式子一定正确。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
inline int read()
{
    int x = 0; bool op = 0;
	char c = getchar();
	while(!isdigit(c))op |= (c == '-'), c = getchar() ;
	while(isdigit(c))x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
	return op ? -x : x;
}

const int N = 5010;
int n, s;
int sumt[N], sumf[N], dp[N];
deque<int> q;

int top(int x, int y)
{
	return dp[x] + sumt[x - 1] * sumf[n] - dp[y] - sumt[y - 1] * sumf[n];
}
int down(int x, int y)
{
	return sumt[x - 1] - sumt[y - 1];
}

int main()
{
	n = read(); s = read();
	for(int i = 1; i <= n; i++)
	{
		sumt[i] = read(); sumf[i] = read();
		sumt[i] += sumt[i - 1]; sumf[i] += sumf[i - 1];
	}
	q.push_back(n + 1);
	for(int i = n; i >= 1; i--)
	{	
		while(q.size() > 1 && top(q[1], q[0]) <= sumf[i - 1] * down(q[1], q[0]))
			q.pop_front();
		int j = q.front();
		int cost = sumt[j - 1] - sumt[i - 1] + s;
		dp[i] = dp[j] + cost * (sumf[n] - sumf[i - 1]);
		int s = q.size() - 1;
		while(q.size() > 1 && top(i, q[s]) * down(q[s], q[s - 1]) >= top(q[s], q[s - 1]) * down(i, q[s]))
		{
			q.pop_back();
			s = q.size() - 1;
		}
		q.push_back(i);
	}
	printf("%d", dp[1]);
	return 0;
}


2020/8/30 15:25
加载中...