• 【2018集训队互测】【XSY3372】取石子


    题目来源:2018集训队互测 Round17 T2

    题意:

    题解:

    显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz!

    显然不会无解……

    为了方便计算石子个数,在最后面加一堆$a_i=c_i=infty$的石子,确保每次取石子都可以取满$k$个;

    先考虑$a_i=0$的情况:

    设$f_{i,j}$表示只考虑第0到$i$堆石子,通关前$j$轮的最少操作次数;

    设$g_{i,j}$表示只考虑第0到$i$堆石子,前$j$轮结束后再取若干次石子,每次取$k$个,使得第$i$堆前面的所有石堆都被取尽的最少操作次数;

    分别记$sa,sb$为$a,b$的前缀和;

    从小到大枚举$i,j$,每次考虑加入石堆$i$的影响,分两种情况转移:

    若存在一种方案使得不取第$i$堆石子即可通关前$j$轮,此时需满足$j imes b_ileq c_i$且$f_{i-1,j} eqinfty$;

    则转移为:

    $f_{i,j}leftarrow f_{i-1,j}$

    $g_{i,j}leftarrow lceilfrac{j imes sb_{i-1}}{k} ceil$

    第二种转移需要满足石子总数够取,即$lceilfrac{j imes sb_{i-1}}{k} ceil imes kleq j imes sb_i$;

    否则枚举最后一次取$i$的轮数$r$,此时最优策略一定是分成三部分:

    1.在前$r$轮取完$[0,i)$中的石子,这部分答案显然为$g_{i,r}$

    2.这轮在$i$中取若干次石子,此时$i$中剩余石子数为$m=r imes sb_i-k imes g_{i,r}$,为了使它在$j$轮后不超过限制,还需要取$x=lceilfrac{max(0,m+(j-r) imes b_i-c_i)}{k} ceil$次;若$x imes k>m$,即石子不够取,则无解;

    3.在剩余的$j-r$轮中取石子,这部分跟第一种情况类似,可以得到$f$的操作数为$f_{i-1,j-r}$,$g$的操作数为$g_{i,j}$;

    因此转移就是:

    $f_{i,j}leftarrow g_{i,r}+f_{i-1,j-r}+x$

    $g_{i,j}leftarrow g_{i,r}+lceilfrac{(j-r) imes sb_{i-1}}{k} ceil+x$

    时间复杂度$O(nt^2)$;

    考虑$a_i eq 0$的情况,可以发现此时对dp的影响只有第二种情况的第三步中,所有石堆的个数都为0;那么直接在$f,g$后面加一维0/1位表示每堆石子初始时是否有值,在第三步转移中用$f_{i-1,j-r,0}$转移,每次计算剩余石子时注意初始的$a_i$即可;

    代码看(chao)了天下第一wxh的代码

    代码:

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 100000000000000000
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 int n,t,k;
    13 ll tmp,tt,m,a[202],b[202],c[202],sa[202],sb[202],f[202][202][2],g[202][202][2];
    14 int main(){
    15     scanf("%d%d%d",&n,&t,&k);
    16     for(int i=1;i<=n;i++){
    17         scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
    18         sa[i]=sa[i-1]+a[i];
    19         sb[i]=sb[i-1]+b[i];
    20     }
    21     n++;
    22     a[n]=c[n]=sa[n]=inf;
    23     sb[n]=sb[n-1];
    24     for(int i=1;i<=n;i++){
    25         for(int j=0;j<=t;j++){
    26             for(int t=0;t<=1;t++){
    27                 f[i][j][t]=g[i][j][t]=inf;
    28                 //transform 1
    29                 if(j*b[i]+t*a[i]<=c[i]&&f[i-1][j][t]!=inf){
    30                     f[i][j][t]=f[i-1][j][t];
    31                     tmp=(j*sb[i-1]+t*sa[i-1]+k-1)/k;
    32                     if(tmp*k<=j*sb[i]+t*sa[i]){
    33                         g[i][j][t]=tmp;
    34                     }
    35                 }
    36                 //transform 2
    37                 for(int r=0;r<j;r++){
    38                     if(g[i][r][t]!=inf){
    39                         m=r*sb[i]+t*sa[i]-k*g[i][r][t];
    40                         tmp=(max(0ll,m+(j-r)*b[i]-c[i])+k-1)/k;
    41                         if(tmp*k<=m&&f[i-1][j-r][0]!=inf){
    42                             f[i][j][t]=min(f[i][j][t],f[i-1][j-r][0]+g[i][r][t]+tmp);
    43                             tt=((j-r)*sb[i-1]+k-1)/k;
    44                             if(tt*k<=(j-r)*sb[i]+m-tmp*k){
    45                                 g[i][j][t]=min(g[i][j][t],g[i][r][t]+tmp+tt);
    46                             }
    47                         }
    48                     }
    49                 }
    50             }
    51         }
    52     }
    53     printf("%lld",f[n][t][1]);
    54     return 0;
    55 }
  • 相关阅读:
    ajax的原理及实现方式
    在linux中添加环境变量
    ftp简单命令
    linux命令之scp
    java中创建对象的方法
    10个调试技巧
    java读取.properties配置文件的几种方法
    Java对象和XML转换
    Java Float类型 减法运算时精度丢失问题
    Java内存分配全面浅析
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/10219190.html
Copyright © 2020-2023  润新知