码迷,mamicode.com
首页 > 其他好文 > 详细

DP3 1012

时间:2019-10-21 13:41:13      阅读:74      评论:0      收藏:0      [点我收藏+]

标签:相交   抽取   open   out   onclick   lamp   ble   分析   play   

游戏

有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

 

DP3 1012

标签:相交   抽取   open   out   onclick   lamp   ble   分析   play   

原文地址:https://www.cnblogs.com/sto324/p/11712674.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!