zoukankan      html  css  js  c++  java
  • DP3 1012

    游戏

    有n个数,编号从1到n。现在把n个数分成k组编号为1到k,使得每组内的数必须连续,组与组之间不能相交并且每个数必须属于一个组。
    游戏进行的过程如下:
    1. 如果n个数都已经获得了,游戏结束。否则,找到编号最小没有全部获得的组X。
    2. 游戏系统会给一个空的盒子,对于组X中已经获得的数i,将ti张写着数i的卡片放入盒子中,对于组X中最小的没有获得的数j,将tj张写着数j的卡片放入盒子中。
    3. 随机从盒子中抽取一张卡片,表示当前获得的数字,然后等待1小时的冷却时间后跳转到过程1。
    现在需要确定一个最好的分组,使得这个游戏期望结束的时间最小。

    对于100%的数据,1 <= n <= 200000, 1 <= k <= min(50, n),1 <= ti <= 100000。

    题解

    对于每一组的时间很好推,就是$sum_{i=1}^{j} frac{sum_{i}}{t_{i}}$

    f[i][j]表示前i个数字分成j组的最小时间,很容易得到dp方程$dp[i][j]=min(dp[k][j-1]+sum_{p=k+1}^{i} frac{sum_{p}-sum_{k}}{t_{p}})$

    化简$f[k][j-1]+sum_{p=k+1}^{i} frac{sum_{p}}{t_{p}}-sum_{p=k+1}^{i} frac{sum_{k}}{t_{p}}$

    $f_{i}=sum_{p=1}^{i} frac{sum_{p}}{t_{p}},g_{i}=sum_{p=1}^{i} frac{1}{t_{p}}$

    所以$dp[i][j]=min(dp[k][j-1]+f[i]-f[k]-sum[k]*(g[i]-g[k]))$

    然后就是斜率优化的一些套路了。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int maxn=200005;
    int n,m;
    int h,t,q[maxn];
    double T[maxn],sum[maxn],f[maxn],g[maxn];
    double dp[55][maxn];
    
    double slope(int k,int p,int j){
        return (dp[j][p]-f[p]+sum[p]*g[p]-dp[j][k]+f[k]-sum[k]*g[k])/(sum[p]-sum[k]);
    }
    
    int main(){
        freopen("game.in","r",stdin);
        freopen("game.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%lf",&T[i]),sum[i]=sum[i-1]+T[i];
        for(int i=1;i<=n;i++){
            f[i]=f[i-1]+sum[i]/T[i];
            g[i]=g[i-1]+1.0/T[i];
        }
        for(int i=1;i<=n;i++) dp[1][i]=f[i];
        for(int j=2;j<=m;j++){
          h=1,t=0;
          q[++t]=0; 
          for(int i=1;i<=n;i++){
              while(h<t&&slope(q[h],q[h+1],j-1)<g[i]) h++;
              int p=q[h];
              dp[j][i]=dp[j-1][p]+f[i]-f[p]-sum[p]*(g[i]-g[p]);
              while(h<t&&slope(q[t-1],q[t],j-1)>slope(q[t],i,j-1)) t--;
              q[++t]=i;
            }
        }
        printf("%.2lf",dp[m][n]);
    }
    game

    开关灯

    有n个灯,初始时都是不亮的状态,每次你可以选择一个某一个灯,不妨记为x,所有满足和x距离不超过k的灯的状态都将被翻转,选择第i个灯的代价记为c[i],问最终所有灯都是亮的状态的最小花费。

    1 <= N <= 10000 , 0 <= k <= 1000 , 0 <= c[i] <= 1000000000。

    题解

    这可能是最简单的一道,可以知道一个灯最多动一次。

    通过感性分析得到翻转区间不能重叠,所以就over了。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=10005;
    const ll inf=100000000000000ll;
    int n,k;
    ll ans,c[maxn];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    ll get(int s){
        ll ret=0;
        for(;s<=n;s+=2*k+1) ret+=c[s];
        s-=2*k+1;
        if(s+k<n) return inf;
        return ret;
    }
    
    int main(){
        freopen("lamp.in","r",stdin);
        freopen("lamp.out","w",stdout);
        read(n);read(k);
        for(int i=1;i<=n;i++) read(c[i]);
        ans=inf;
        for(int i=1;i<=k+1;i++) ans=min(ans,get(i));
        printf("%lld",ans);
    }
    lamp
  • 相关阅读:
    关于PCA算法的一点学习总结
    c++大作业--学籍管理系统--
    AVC1与H264的差别
    Java多线程之wait(),notify(),notifyAll()
    jquery——zTree, 完美好用的树插件
    一位Erlang程序猿的自白
    SQL Server 中关于EXCEPT和INTERSECT的使用方法
    JAVA程序猿怎么才干高速查找到学习资料?
    CFileDialog的使用方法简单介绍
    XCode 打包问题巧遇
  • 原文地址:https://www.cnblogs.com/sto324/p/11712674.html
Copyright © 2011-2022 走看看