zoukankan      html  css  js  c++  java
  • 【BZOJ1110】[POI2007] 砝码(进制分解)

    点此看题面

    大致题意:(n)个背包和(m)个砝码,每个背包有一定的重量限制,而砝码两两之间重量都成倍数关系。求最多能把几个砝码装入背包。

    前言

    (Jan 29th)刷题计划(5/6),算法标签:贪心。

    一道有技巧的贪心,一道挺有趣的题目。

    进制分解

    考虑题目中“砝码两两之间重量都成倍数关系”这句话肯定有猫腻,必然要以它为入手点。

    将砝码按重量排序并去重后,易知每一个砝码重量至少是前一个砝码的两倍,因此砝码重量的种数总共也只有(log_2(10^9)≈30)种,是非常少的。

    设排序去重后第(i)种砝码重量为(a_i),个数为(b_i)

    我们定义一个特殊的进制,其中第(i)位的位权是(a_i)

    然后我们把背包的容量都按照这种特殊的进制进行进制分解,然后分每一位加在一起。注意,加的过程中不能进位,因为每个背包是独立的。

    最后,我们贪心地从小到大枚举每一种砝码。

    对于第(i)种砝码,我们操作(b_i)次,每次将第(i)位减(1)。若减成了负数,则去向更高位借位。若无法再借,就说明无法再加砝码。

    注意,第(i+1)位的(1)退到第(i)位,会变成(frac{a_{i+1}}{a_i})

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int n,m,k,s[N+5],a[N+5],b[N+5],p[N+5];
    I bool Dec(CI x)//将第x位减1
    {
    	RI i;for(i=x;i<=k&&!p[i];++i);if(i>k) return 0;//如果借不到,返回0
    	for(--p[i--];i>=x;--i) p[i]+=a[i+1]/a[i]-1;return 1;//借位,返回1
    }
    int main()
    {
    	RI i,j,t=0;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",s+i);
    	for(i=1;i<=m;++i) scanf("%d",a+i);sort(a+1,a+m+1);//排序
    	for(i=1;i<=m;++i) a[i]^a[i-1]?(a[++k]=a[i],b[k]=1):(++b[k]);//去重,同时统计每种砝码的个数
    	for(i=1;i<=n;++i) for(j=k;j;--j) p[j]+=s[i]/a[j],s[i]%=a[j];//进制分解
    	for(i=1;i<=k;++i) for(j=1;j<=b[i];++j) if(Dec(i)) ++t;else goto End;//枚举砝码尝试放入
    	End:return printf("%d",t),0;//输出答案
    }
    
  • 相关阅读:
    点分治 (等级排) codeforces 321C
    树上点分治 poj 1741
    判断点在直线的左侧还是右侧
    树的重心
    链式前向星
    树上点的分治
    构造 素数
    二进制 + 模拟
    枚举 + 三分 (游标)
    枚举 + 三分
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1110.html
Copyright © 2011-2022 走看看