专栏文章

任务安排 1~3 Solute&Code

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip6n46o
此快照首次捕获于
2025/12/03 07:01
3 个月前
此快照最后确认于
2025/12/03 07:01
3 个月前
查看原文

1

C
class Solotion_Case
{
	/*
	首先考虑最基本的思路:
	先求出s,t的前缀和,然后设f[i,j]表示把前i个任务分成j批执行的最小费用
	这样就可以列出n三次方的方程,见algo P352

	来一个小优化:
	由于本题目没有规定需要把任务分成多少批次,在上一个解法之中之所以需要批数j,
	是因为我们需要知道机器启动了多少次,从而计算出当前任务的完成时刻,那么考虑能否直接获取每个任务执行时候的开始时间
	⭐机器执行某一批任务而花费的启动时间S,会累加到在此之后所有任务的完成时刻上面。
	设f[i]表示把前i个任务分成若干批执行的最小费用,有方程:
	f[i] = min{ f[j] + sumT[i]*(sumC[i]-sumC[j]) + S*(sumC[N]-sumC[j]) },其中0 ≤ j<i,sumT[i]是忽略机器的启动时间时,这批任务的完成时刻

	*/
};
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define inf 0x3f
using namespace std;
class Quick_IO
{
  public:
	template<typename T>
	void read(T &r)
	{
		T x = 0, f = 1;
		char ch = getchar();
		while (ch < '0' || ch > '9')
		{
			if (ch == '-') f = -1;
			ch = getchar();
		}
		while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
		r = x * f;
	}
	template<typename T, typename... Args>
	void read(T &tmp, Args &... tmps)
	{
		read(tmp);
		read(tmps...);
	}
} io;

int n, S;
ll f[N], st[N], sc[N];
#define sumc(i,j) (sc[i] - sc[j]) //注意!!define直接把代码移下来,需要加括号

signed main()
{
	io.read(n, S);
	for (int i = 1, t, c; i <= n; i++)
	{
		io.read(t, c);
		st[i] = st[i - 1] + t;
		sc[i] = sc[i - 1] + c;
	}

	memset(f, inf, sizeof f);
	f[0] = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j < i; j++)
			f[i] = min(f[i], f[j] + st[i] * sumc(i, j) + S * sumc(n, j));

	cout << f[n];
}

2

C
class Solotion_Case
{
	/*
	基本思路见任务安排1,注意本题T1的n方代码即可过。

	1.考虑优化:
	先把方程拆开成多项式,去掉min,把关于j的值看作变量,其余部分看作常数
		f[j] = ( S+sumT[i] ) * sumC[j] + { f[i] - sumT[i]*sumC[i] - S*sumC[N] }
		可以得到:k=( S+sumT[i] );b={ f[i] - sumT[i]*sumC[i] - S*sumC[N] }
	因此每个决策的点都对应着坐标系的一个点(sumC[j],f[j]);每个待求解的状态f[i]都对应着一条直线的截距,而斜率固定
	目的是找到最小截距(k定,平移线段)

	2.
	由于前缀和的单调性(体现在坐标中x,y的单调性),我们只需要维护一个点集合,满足任意三个点都符合下凸关系
	即维护一个凸壳,这样凸壳的最左边的端点(也是最低的),就是最小的截距对应的线过的点
	因为对于每个需要求解的状态来说,斜率是递增的。

	3.
	因此可以使用单调队列来维护球壳,满足球壳里面最低两点的斜率大于这个状态的斜率,来让最左点符合条件
	考虑维护这个队列:
		每次i++,都会加入一个新的决策点到坐标系上,同时斜率增大。
		最左边的点如果和下一个点的斜率小于当前要求解的状态斜率,说明不符合要求,那么删左边直到符合要求(出队)。
		这时候最左边的点就是最优解,直接更新状态。
		然后把新决策放在右面(队尾),注意这个操作也需要维护:
		如果右面放了以后不符合凸壳,也就是说这个要加入的点的斜率比目前最右边的点要更小(点更低),那么把队尾出队直到满足凸壳。

	非常妙的思路!!
	复杂度:N
	*/
};
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define inf 0x3f
using namespace std;
class Quick_IO
{
  public:
	template<typename T>
	void read(T &r)
	{
		T x = 0, f = 1;
		char ch = getchar();
		while (ch < '0' || ch > '9')
		{
			if (ch == '-') f = -1;
			ch = getchar();
		}
		while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
		r = x * f;
	}
	template<typename T, typename... Args>
	void read(T &tmp, Args &... tmps)
	{
		read(tmp);
		read(tmps...);
	}
} io;

int n, S;
ll f[N], st[N], sc[N];
int q[N];

signed main()
{
	io.read(n, S);
	for (int i = 1, t, c; i <= n; i++)
	{
		io.read(t, c);
		st[i] = st[i - 1] + t;
		sc[i] = sc[i - 1] + c;
	}

	memset(f, inf, sizeof f);
	f[0] = 0;
	int l = 1, r = 1;
	q[1] = 0;

	for (int i = 1; i <= n; i++)
	{
		while (l < r && (f[q[l + 1]] - f[q[l]]) <= (S + st[i]) * (sc[q[l + 1]] - sc[q[l]])) l++;	//除法移项变乘
		int j = q[l];
		f[i] = f[j] - (S + st[i]) * sc[j] + st[i] * sc[i] + S*sc[n];
		while (l < r && (f[q[r]] - f[q[r - 1]]) * (sc[i] - sc[q[r]]) >= (f[i] - f[q[r]]) * (sc[q[r]] - sc[q[r - 1]])) r--;
		q[++r] = i;
	}

	cout << f[n];
}

3

C
class Solotion_Case
{
	/*
	在T2的基础上,我们发现任务的执行时间可能是负数,意味着斜率不再具有单调性。
	所以我们不能只在单调队列上维护比当前斜率大的点,而是应该维护一个完整的凸壳。
	这样的话队头就不一定是最优的选择了,可以在整个点集里面二分查找最合适的位置,
	让这个位置满足左侧线段的斜率比S+sumT[i]小,右侧线段的斜率比他大,就是在当前待求解直线以上的最凸点
	队尾维护的方法同T2。

	复杂度O(NlogN)
	*/
};
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define inf 0x3f
using namespace std;
class Quick_IO
{
  public:
	template<typename T>
	void read(T &r)
	{
		T x = 0, f = 1;
		char ch = getchar();
		while (ch < '0' || ch > '9')
		{
			if (ch == '-') f = -1;
			ch = getchar();
		}
		while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
		r = x * f;
	}
	template<typename T, typename... Args>
	void read(T &tmp, Args &... tmps)
	{
		read(tmp);
		read(tmps...);
	}
} io;

int n, S;
ll f[N], st[N], sc[N];
int q[N], l, r;

int Binary_Search(int i, int k)
{
	if (l == r) return q[l];
	int L = l, R = r;
	while (L < R)
	{
		int mid = (L + R) / 2;
		if (f[q[mid + 1]] - f[q[mid]] <= k * (sc[q[mid + 1]] - sc[q[mid]])) L = mid + 1;
		else R = mid;
	}
	return q[L];
}

signed main()
{
	io.read(n, S);
	for (int i = 1, t, c; i <= n; i++)
	{
		io.read(t, c);
		st[i] = st[i - 1] + t;
		sc[i] = sc[i - 1] + c;
	}

	memset(f, inf, sizeof f);
	f[0] = 0;
	q[1] = 0, l = 1, r = 1;

	for (int i = 1; i <= n; i++)
	{
		int j = Binary_Search(i, S + st[i]);
		f[i] = f[j] - (S + st[i]) * sc[j] + st[i] * sc[i] + S*sc[n];
		while (l < r && (f[q[r]] - f[q[r - 1]]) * (sc[i] - sc[q[r]]) >= (f[i] - f[q[r]]) * (sc[q[r]] - sc[q[r - 1]])) r--;
		q[++r] = i;
	}

	cout << f[n];
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...