• 【神仙题】【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转移时,可以通过代数恒等式证明出一种正确的转移方法。

  • 相关阅读:
    提高情商的八种方法
    线程安全与可重入
    【Linux必知必会】initrd.img、vmlinux和 vmlinuz************
    shell调试技术
    (转)DeviceIOControl详解
    软件质量特性及其子特性列表
    【Linux必知必会】initrd.img、vmlinux和 vmlinuz
    驱动程序与应用程序之间共享内存
    调试器GDB
    知道IP地址和子网掩码。算出网络地址、广播地址、地址范围、可用的主机数
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9879795.html
Copyright © 2020-2023  润新知