zoukankan      html  css  js  c++  java
  • [SDOI2008]Sue的小球 题解

    Sue的小球 题解

    题意

    (~~~~) 给了 (n) 个小球,第 (i) 个小球在 (x_i) 初始分值为 (y_i) ,同时它会以 (dfrac{v_i}{1000}/ exttt{单位时间}) 的速度降低。(可以降低为负数)从初始位置开始,每个单位时间可以移动 (1) 单位长度。收集小球不需要时间,求收集所有小球后能获得的最大分值。


    题解

    (~~~~) 首先,可以想到对每个球按位置从小到大排序。由于总的得分固定,我们可以求最小损失,然后考虑:对于两个小球 (l,r (x_l<x_r)) ,若它们都被收集了,则小球 (xin[l,r]) 也被收集一定是最优方案:

    (~~~~) 证明:显然。 每个小球被收集,一定是从起始点走到过该小球的,则此时顺手收集路上的小球一定不会使答案变劣,因为收集不需要时间。

    (~~~~) 所以,我们考虑已经收集了 ([l,r]) 的小球时,下一步一定是扩展到 (l-1)(r+1) ,不难想到定义 ( exttt{DP}) 状态:令 (dp_{i,j}) 表示已经收集 ([l,r]) 的所有小球时的最小损失。考虑 ( exttt{DP}) 转移时需要当前的位置,因此增加一维,用来记录最后停留在 (i) 或者 (j) (当然也可以定义两个数组)。

    (~~~~) 发现还是有问题, ( exttt{DP}) 转移还需要记录当前的时间,可是如果我们把时间也加在 ( exttt{DP}) 维度上,时间就爆了。

    (~~~~) 这时就轮到这道题的精髓了——提前计算。我们是可以算出每一次转移时的时间的,那如果我们把每次转移时其他小球损失的分数也加上去,既然每个小球都将会被收集,那么最后的效果其实是一样的。

    (~~~~) 具体来说,如果有小球 ([l,r]) 已经被收集,则下一次转移时 ([1,l))((r,n]) 的所有会被消耗的分数都可以直接加在下一次。

    (~~~~) 若设:(pre_{i,j}) 表示 (sum_{k=i}^j v_k) ,则有 ( exttt{DP}) 转移方程。

    [Large left{egin{array}{l} dp_{i,j,0}=min(~dp_{i+1,j,0}+(x_{i+1}-x_i) imes(pre_{1,n}-pre_{i+1,j})~,~dp_{i+1,j,1}+(x_j-x_i) imes (pre_{1,n}-pre_{i+1,j})~)\ dp_{i,j,1}=min(~dp_{i,j-1,0}+(x_j-x_i) imes(pre_{1,n}-pre_{i,j-1})~,~dp_{i,j-1,1}+(x_j-x_{j-1}) imes(pre_{1,n}-pre_{i,j-1})~) end{array} ight. ]

    (~~~~) 然后转移就好了。

    (~~~~) 注意处理初始值时可以增加一个小球使得它的 (x=x_0,y=0,v=0) ,初始化时就令这个小球为 (0) 即可。


    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    template<typename T>void read(T &x)
    {
        T f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    struct node{
    	int x,y,v;
    }b[1005];
    inline bool cmp(node x,node y) {return x.x<y.x;}
    ll dp1[1005][1005],dp2[1005][1005],w[1005];
    inline int Abs(int x){return x>0?x:-x;}
    int calc(int l,int r){return w[r]-w[l-1];}
    int main() {
    	int n,x;
    	ll tot=0;
    	read(n);read(x);
    	for(int i=1;i<=n;i++) read(b[i].x);
    	for(int i=1;i<=n;i++) read(b[i].y),tot+=b[i].y;
    	for(int i=1;i<=n;i++) read(b[i].v);
    	b[++n].x=x;
    	sort(b+1,b+n+1,cmp);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=n;j++) dp1[i][j]=dp2[i][j]=1e18;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		w[i]=w[i-1]+b[i].v;
    		if(b[i].v==0&&b[i].x==x) dp1[i][i]=dp2[i][i]=0;
    	}
    	//dp1[i][j] 收集了第 i个 至 第 j个彩蛋 最后停留在 i
    	//dp2[i][j]··················· j
    	for(int len=2;len<=n;len++)
    	{
    		for(int i=1;i+len-1<=n;i++)
    		{
    			int j=i+len-1;
    			dp1[i][j]=min(dp1[i][j],dp1[i+1][j]+Abs(b[i+1].x-b[i].x)*(calc(1,i)+calc(j+1,n)));
    			dp1[i][j]=min(dp1[i][j],dp2[i+1][j]+Abs(b[ j ].x-b[i].x)*(calc(1,i)+calc(j+1,n)));
    			dp2[i][j]=min(dp2[i][j],dp1[i][j-1]+Abs(b[ i ].x-b[j].x)*(calc(1,i-1)+calc(j,n)));
    			dp2[i][j]=min(dp2[i][j],dp2[i][j-1]+Abs(b[j-1].x-b[j].x)*(calc(1,i-1)+calc(j,n)));
    		}
    	}
    //	printf("%d %d %d
    ",tot-min(dp1[1][n],dp2[1][n]),dp1[1][n],dp2[1][n]);
    	printf("%.3lf",(tot-min(dp1[1][n],dp2[1][n]))/1000.0);
    	return 0;
    }
    
    

    (~~~~)

  • 相关阅读:
    eclipse真机调试显示Target unknown的解决方法
    教你看懂GERBER中的钻孔(.txt)文件
    Quartus ii 12.0 和ModelSim 10.1 SE安装及连接
    Android的学习——ubuntu下android5.1源码的make编译
    ubuntu 14.04 下找不到命令,路径出错
    【转载】VMware虚拟机修改硬盘容量大小
    Fedora10下建立linux系统的窗口没有地址栏
    [转]SecureCRT连接主机时,无法从键盘输入
    在FASTBuild中使用Distribution
    在FASTBuild中使用Caching
  • 原文地址:https://www.cnblogs.com/Azazel/p/13685995.html
Copyright © 2011-2022 走看看