• CF311B Cats Transport


    XVII.CF311B Cats Transport

    推式子时间到~~~

    我们首先对题目中的\(d_i\)做前缀和,求出每座山距离原点距离;

    然后对于第\(i\)只猫,如果一个饲养员在\(t_i-d_{h_i}\)时刻以后出发就可以接到它;

    注意,饲养员可以在负时刻就出发!!!我之前想多了以为只能在非负时刻出发而纳闷了好半天

    我们设\(t_i-d_{h_i}\)为新的\(t_i\),然后将所有的\(t_i\)排序。

    然后开始DP:

    \(f[i][j]\)表示:前\(i\)只猫,派出\(j\)个人,的最优时间。再设\(s_i\)表示\(t_i\)的前缀和。

    则有\(f[i][j]=\min\limits_{k=0}^{i-1}\{f[k][j-1]+t_i*(i-k)-(s_i-s_k)\}\)

    我们这样就可以写出\(O(m^2p)\)的代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,p,d[100100],t[100100],s[100100],f[100100][110],qwq=0x3f3f3f3f3f3f3f3f;
    signed main(){
    	scanf("%lld%lld%lld",&n,&m,&p),memset(f,0x3f3f3f3f,sizeof(f));
    	for(int i=2;i<=n;i++)scanf("%lld",&d[i]),d[i]+=d[i-1];
    //	for(int i=1;i<=n;i++)printf("%lld ",d[i]);puts("");
    	for(int i=1,x,y;i<=m;i++)scanf("%lld%lld",&x,&y),t[i]=y-d[x];
    //	printf("%lld\n",res);
    //	for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
    	sort(t+1,t+m+1);
    	for(int i=1;i<=m;i++)s[i]=s[i-1]+t[i];
    //	for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
    	f[0][0]=0;
    	for(int j=1;j<=p;j++)for(int i=1;i<=m;i++)for(int k=0;k<i;k++)f[i][j]=min(f[i][j],f[k][j-1]+(i-k)*t[i]-(s[i]-s[k]));
    	for(int i=1;i<=p;i++)qwq=min(qwq,f[m][i]);
    	printf("%lld\n",qwq);
    	return 0;
    }
    

    然后考虑斜率优化一下:

    \(f[i][j]\)中,我们按照列优先(先枚举\(j\))的顺序进行DP;并且,设\(f[i][j-1]\)\(F[i]\)

    \(j<k<i\)(不是同一个\(j\))。则如果\(j\)\(k\)优,则有:

    \(F_j+t_i*(i-j)-(s_i-s_j)<F_k+t_i*(i-k)-(s_i-s_k)\)

    拆开

    \(F_j+i*t_i-j*t_i-s_i+s_j<F_k+i*t_i-k*t_i-s_i-s_k\)

    抵消

    \(F_j-j*t_i+s_j<F_k-k*t_i+s_k\)

    移项

    \(F_j-F_k+s_j-s_k<(j-k)*t_i\)

    除过去(注意\(j-k\)是负数)

    \(\dfrac{F_j-F_k+s_j-s_k}{j-k}>t_i\)

    右边的\(t_i\)是递增的(排过序了),因此可以采用单调队列维护斜率;然后维护一个下凸壳即可。复杂度\(O(mp)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,p,d[100100],t[100100],s[100100],f[100100][110],qwq=0x3f3f3f3f3f3f3f3f,l,r,q[100100];
    signed main(){
    	scanf("%lld%lld%lld",&n,&m,&p),memset(f,0x3f3f3f3f,sizeof(f));
    	for(int i=2;i<=n;i++)scanf("%lld",&d[i]),d[i]+=d[i-1];
    //	for(int i=1;i<=n;i++)printf("%lld ",d[i]);puts("");
    	for(int i=1,x,y;i<=m;i++)scanf("%lld%lld",&x,&y),t[i]=y-d[x];
    //	printf("%lld\n",res);
    //	for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
    	sort(t+1,t+m+1);
    	for(int i=1;i<=m;i++)s[i]=s[i-1]+t[i];
    //	for(int i=1;i<=m;i++)printf("%lld ",t[i]);puts("");
    	f[0][0]=0;
    	for(int j=1;j<=p;j++){
    		l=r=0;
    		for(int i=1;i<=m;i++){
    			while(r-l&&(f[q[l]][j-1]-f[q[l+1]][j-1]+s[q[l]]-s[q[l+1]])>=(q[l]-q[l+1])*t[i])l++;
    			f[i][j]=f[q[l]][j-1]+(i-q[l])*t[i]-(s[i]-s[q[l]]);
    			while(r-l&&(f[q[r-1]][j-1]-f[q[r]][j-1]+s[q[r-1]]-s[q[r]])*(q[r]-i)>=(f[q[r]][j-1]-f[i][j-1]+s[q[r]]-s[i])*(q[r-1]-q[r]))r--;
    			q[++r]=i;
    		}
    	}
    	for(int i=1;i<=p;i++)qwq=min(qwq,f[m][i]);
    	printf("%lld\n",qwq);
    	return 0;
    }
    

  • 相关阅读:
    关于地址添加 子窗体向父窗体传值 树的使用详细
    C# 常见面试题2
    微软2014实习生及秋令营技术类职位在线测试
    【面试题004】c/c++字符串,替换空格
    【面试题003】c数组做为参数退化的问题,二维数组中的查找
    【面试题001】类型转换关键字,空类对象模型,拷贝构造函数,赋值运算符函数
    【面试题002】java实现的单例模式,c++实现单例模式,实现禁止拷贝
    【设计模式6原则总结】
    【设计模式六大原则6】开闭原则(Open Close Principle)
    【设计模式六大原则5】迪米特法则(Law Of Demeter)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14596914.html
Copyright © 2020-2023  润新知