zoukankan      html  css  js  c++  java
  • [SCOI2014]方伯伯的商场之旅

    Description

    方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。
    现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。
    例如:10 进制下的位置在 12312 的人,合并石子的最少代价为:
    1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9
    即把所有的石子都合并在第三堆

    Input

    输入仅有 1 行,包含 3 个用空格分隔的整数 L,R,K,表示商场给方伯伯的 2 个整数,以及进制数

    HINT

     1 < =  L < =  R < =  10^15, 2 < =  K < =  20

    Solution

    说白了,这个题就是给了L~R的数,每个数的每个数位是一堆石子,把这堆石子合成一个位置,求总的最小代价。

    法一:GZZ法

    发现,对于一个数字P,假设钦定最终合并位置是p,

    调整的时候,p向左移动一位,代价变化是p及右边所有的数位和-p左边所有数位和。

    p向右移动一位,代价变化是p及左边所有数位和-p右边所有数位和。

    设最优的位置的数字是x,位置是p,p左边数位和是a,右边是b

    那么,一定有不等式:x+a-b>=0 ; x+b-a>=0 就是说,x不论往左往右移动,代价的变化总是增大的。

    即:-x<=a-b<=x

    所以,如果知道最终填的a-b,和x,p,就可以判断这个p位置填x是不是左边a,右边b的最优解了。

    枚举p,x;

    伪代码:(cnt是最高位,进制用m,填数用k)

    for(p=1~cnt)

      for(x=0~m-1)

        for(i=cnt~1)

       for(a-b=-200~+200)

      设f[i][a-b][0/1]表示,填完第i位,a-b的值,有没有限制情况下,所有符合情况的数移动到p位置所花费的代价。

              g[i][a-b][0/1]表示,f的方案数,即满足情况的数的个数,方便转移。

           if(i==p){

        

        continue;

      }

           for(k=0;k<m;k++){

        if(i<p)

        else

      }

     在i循环完之后,

     for(a-b=-200~+200)

          if(-x<=a-b<x) ret+=f[1][a-b][0/1]

     注意这里是<=和<,因为可能一个数字有两个位置都是最优的合并位置,只能算一遍。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=70;
    const int M=22;
    const int fix=201;
    const int up=402;
    ll f[N][405][2];
    ll g[N][405][2];
    ll L,R;
    int m;
    ll ansl,ansr;
    int a[N],cnt;
    ll wrk(){
        ll ret=0;
        for(int p=1;p<=cnt;p++){
           for(int x=0;x<m;x++){
            memset(f,0,sizeof f);
            memset(g,0,sizeof g);
            g[cnt+1][fix][1]=1;
            for(int i=cnt;i>=1;i--){
                for(int j=0;j<=up;j++){
                        if(i==p){
                            if(x<a[i]){
                                if(g[i+1][j][0]) g[i][j][0]+=g[i+1][j][0],f[i][j][0]+=f[i+1][j][0];
                                if(g[i+1][j][1]) g[i][j][0]+=g[i+1][j][1],f[i][j][0]+=f[i+1][j][1];
                            }                        
                            else if(x==a[i]){
                                g[i][j][1]+=g[i+1][j][1],f[i][j][1]+=f[i+1][j][1];
                                g[i][j][0]+=g[i+1][j][0],f[i][j][0]+=f[i+1][j][0];
                            }
                            else{
                                g[i][j][0]+=g[i+1][j][0],f[i][j][0]+=f[i+1][j][0];
                            }
                            continue;
                        }
                        
                    for(int k=0;k<m;k++){
                        if(i>p){//before
                            if(j+k>up) continue;
                            
                            if(k<a[i]){
                                g[i][j+k][0]+=g[i+1][j][0],f[i][j+k][0]+=f[i+1][j][0]+(i-p)*k*g[i+1][j][0];
                                g[i][j+k][0]+=g[i+1][j][1],f[i][j+k][0]+=f[i+1][j][1]+(i-p)*k*g[i+1][j][1];
                            }
                            else if(k==a[i]){
                                g[i][j+k][0]+=g[i+1][j][0],f[i][j+k][0]+=f[i+1][j][0]+(i-p)*k*g[i+1][j][0];
                                g[i][j+k][1]+=g[i+1][j][1],f[i][j+k][1]+=f[i+1][j][1]+(i-p)*k*g[i+1][j][1];
                            }
                            else{
                                g[i][j+k][0]+=g[i+1][j][0],f[i][j+k][0]+=f[i+1][j][0]+(i-p)*k*g[i+1][j][0];
                            }
                        }
                        else{//after 
                            if(j-k<0) continue;
                        
                        
                            if(k<a[i]){
                                f[i][j-k][0]+=f[i+1][j][0]+g[i+1][j][0]*(p-i)*k,g[i][j-k][0]+=g[i+1][j][0];
                                f[i][j-k][0]+=f[i+1][j][1]+g[i+1][j][1]*(p-i)*k,g[i][j-k][0]+=g[i+1][j][1];
                            }
                            else if(k==a[i]){
                                f[i][j-k][0]+=f[i+1][j][0]+g[i+1][j][0]*(p-i)*k,g[i][j-k][0]+=g[i+1][j][0];
                                f[i][j-k][1]+=f[i+1][j][1]+g[i+1][j][1]*(p-i)*k,g[i][j-k][1]+=g[i+1][j][1];
                            }
                            else{
                                f[i][j-k][0]+=f[i+1][j][0]+g[i+1][j][0]*(p-i)*k,g[i][j-k][0]+=g[i+1][j][0];
                            }
                        }
                    }
                }
            }
                for(int j=0;j<=up;j++){
                    if((fix-x<=j)&&(j<x+fix)){
                        ret+=f[1][j][0]+f[1][j][1];            
                    }
                }
            }
        }    
        return ret;
    }
    int main(){
        scanf("%lld%lld",&L,&R);
        scanf("%d",&m);
        L--;
        cnt=0;
        while(L){
            a[++cnt]=L%m;
            L/=m;
        }
        if(cnt==0){
            ansl=0;
        }
        else{
            ansl=wrk();
        }
        
        cnt=0;
        while(R){
            a[++cnt]=R%m;
            R/=m;
        }
        ansr=wrk();
        printf("%lld",ansr-ansl);
    }

    法二:大众法。

    直接钦定1号位置是最优位置,计算出来所有的总和ans

    调整。

    枚举位置p从2~cnt,表示要计算从p-1移动到p,会有多少个数的代价减少多少。

    代价就是,sum(1,p-1)-sum(p,cnt)

    设f[i][a-b][0/1]表示,第i位,这个sum的差值,有没有限制情况下,多少个数符合这个情况。

    循环完一个p之后,

    把a-b<0的f,ans-=(a-b)*f[i][a-b][0/1]

    a-b>=0的不管。

    这样进行cnt次,一定可以把所有的数移动到最优解的位置。

    网上题解很多,代码就不贴了。(我也没写)

  • 相关阅读:
    ActiveMQ 5.x 消息队列
    Spring Boot 整合 ElasticSearch 框架
    Spring Boot 整合 Logback 日志框架
    Spring Boot 整合定时任务和异步任务处理
    Spring 中使用 Java 5.0 Executor
    二级指针三种内存模型综合训练
    08-图8 How Long Does It Take (25 分)
    08-图9 关键活动 (30 分)
    08-图7 公路村村通 (30 分)
    C函数之index、strtoul
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9526948.html
Copyright © 2011-2022 走看看