【神仙题】【CF28D】 Don't fear, DravDe is kind

传送门

Description

一个有N辆卡车的车队从城市Z驶向城市3,来到了一条叫做“恐惧隧道”的隧道。在卡车司机中,有传言说怪物DravDe在那条隧道里搜寻司机。有些司机害怕先走,而其他人则害怕后走。但让我们考虑一般情况,每辆卡车用四个数字描述:

•v,卡车的价值,包括乘客和货物
•c,乘客数量,包括司机本人
•l,当前辆卡车之前应该进入的隧道的总人数,这样,当前司机就可以克服他的恐惧(如果怪物出现在那辆车前面,它会先吃掉他们)
•r,当前辆卡车之后应该进入的隧道的总人数,这样,当前司机就可以克服他的恐惧(如果怪物出现在那辆车后面,它会先吃掉他们)

由于路面很窄,如果 DravDe 一旦出现,就不可能逃离。此外,车队不能重新安排。卡 车的顺序是不能改变的,但是有卡车会停留在隧道附近无限期。你,作为车队的头儿,应该 把一些卡车移走(是的,忽略这些卡车的价值),这样车队的其余部分可以通过隧道。求移 走一些卡车后,剩余卡车的总价值最大是多少。

Input

第一行是卡车个数(n)

下面(n)行,每行四个数,按照上述顺序给出卡车的四个参数

Output

共输出两行。第一行输出代表价值最大时剩余的卡车数量

第二行输出被选择的卡车编号。任意输出一种即可

Hint

(0~leq~v~leq~10^4)(0~leq~)其他参数(leq~10^5)

Solution

经过一番深思熟虑,我们发现对于同时被选择的两辆车,总的人数应该是(c+r+l)。于是得出结论,被同时选择的车上述参数和应该相同。

于是这告诉我们只有满足上述条件才能更新答案,以及对于一辆车,他所在的被选择的序列的人数应该是固定的。

显然这是一个线性DP。于是有转移方程(f_i=max{f_j}+v_i),其中满足(j)的参数和与(i)相同,同时因为(j)(i)的前面,于是有(l_j+c_i=l_i)。按照这个方程即可进行转移。每枚举到一个(r=0)的位置就可以更新答案。

Code

#include<map>
#include<cstdio>
#include<algorithm>
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

template <typename T>
inline void qr(T &x) {
	rg char ch=getchar(),lst=' ';
	while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
	while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(lst == '-') x=-x;
}

namespace IO {
	char buf[120];
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
	if(x < 0) {x=-x,putchar('-');}
	rg int top=0;
	do {IO::buf[++top]=x%10+'0';} while(x/=10);
	while(top) putchar(IO::buf[top--]);
	if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T a,const T b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}

template <typename T>
inline void mswap(T &_a,T &_b) {
	T _temp=_a;_a=_b;_b=_temp;
}

const int maxn = 400010;

struct M {
	int v,c,l,r,sum,pos;
	inline bool operator<(const M &_others) const {
		if(this->sum != _others.sum) return this->sum < _others.sum;
		return this->pos < _others.pos;
	}
};
M MU[maxn];

int n,ans;
int frog[maxn],pre[maxn],pcnt[maxn];
std::map<int,int>mp[maxn];

void dfs(ci);

int main() {
	qr(n);
	for(rg int i=1;i<=n;++i) {
		M &now=MU[i];
		qr(now.v);qr(now.c);qr(now.l);qr(now.r);
		now.sum=now.c+now.l+now.r;now.pos=i;
	}
	std::sort(MU+1,MU+1+n);
	for(rg int i=1;i<=n;++i) {
		int k;
		if(!MU[i].l) {
			frog[i]=MU[i].v;pcnt[i]=1;
		}
		else if((k=mp[MU[i].sum][MU[i].l]) != 0) {
			frog[i]=frog[k]+MU[i].v;pre[i]=k;pcnt[i]=pcnt[k]+1;
		}
		if(frog[i] && (frog[i] > frog[mp[MU[i].sum][MU[i].c+MU[i].l]])) mp[MU[i].sum][MU[i].l+MU[i].c]=i;
		if(!(MU[i].r) && (frog[ans] < frog[i])) ans=i;
	}
	qw(pcnt[ans],'
',true);
	dfs(ans);putchar('
');
	return 0;
}

void dfs(ci x) {
	if(!x) return;
	dfs(pre[x]);
	qw(MU[x].pos,' ',true);
}

Summary

在DP转移时,可以通过代数恒等式证明出一种正确的转移方法。

原文地址:https://www.cnblogs.com/yifusuyi/p/9879795.html