zoukankan      html  css  js  c++  java
  • Present [神奇 背包->最短路]

    PresentPresent


    color{red}{正解部分}

    先设 p1p_1 表示最小的 pp .

    pxp_x 使用了 p1p_1 次时, 可以用 pxp_xp1p_1 替代,
    举个例子, 假如当前选物品的情况是 i=2N(p11)pisumlimits_{i=2}^N(p_1-1)p_i, 此时只要在 x[2,N]x∈[2, N] 中再选出任意一个物品 pxp_x,
    p1p_1pxp_x 就可以被 pxp_xp1p_1 顶替 .

    由此可以想到一个比暴力更优的背包dpdp方法,
    使用总容量 i=1Np1pisumlimits_{i=1}^Np_1p_i 的背包进行 dpdp, 然后对于每个 aia_i 的判断, 只需将 aia_i 模上 i=1Np1pisumlimits_{i=1}^Np_1p_i (等同于i=1Np1pisumlimits_{i=1}^Np_1p_i全部被 p1p_1 顶替了), 此时 aia_i 一定是在背包容量内的, 直接判断即可 .

    这样可以有 60pts60pts .

    使用 i=1Np1pisumlimits_{i=1}^Np_1p_i 为上界的原因是 i=1N(p11)pisumlimits_{i=1}^N(p_1-1)p_ip1p_1 不能消任何数字的极限情况, 以 i=1Np1pisumlimits_{i=1}^Np_1p_i 为上界就可以顾及到所有的装填方法 .


    思考能否以更小的背包容量解决这个问题,

    x=ymod  p1x=y mod p_1, 且 x<yx < y, 则 yx=0mod  p1y - x = 0 mod p_1,
    即对于 mod p1mod p_1 相同的x,yx, y, 要凑成 yy, 可以先凑成 xx, 然后用 p1p_1xx 的基础上填充 凑成 yy,
    换句话说, 若两个数模 p1p_1 的值相同, 且除 p1p_1 较小的那个数字可以凑出, 则较大的那个数字也可以凑出 .
    上面这句话是 下方法的思想 核心 .

    由此可以设 F[x]F[x] 表示 所有满足 i%p1=xi\%p_1 = x 且能被凑出的 ii 中, 最小的 ip1lfloor frac{i}{p_1} floor,
    用类似最短路的算法转移, 最后只需检查当前数字aia_i是否满足条件 F[ai%p1]aip1F[a_i \% p_1] le lfloor frac{a_i}{p_1} floor 即可 .

    时间复杂度 O(p1logp1)O(p_1logp_1) .


    color{red}{实现部分}

    #include<bits/stdc++.h>
    #define reg register
    #define fi first
    #define se second
    typedef std::pair<int, int> pr;
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    int N;
    int M;
    int Ans;
    int p[505];
    int a[300005];
    int F[200005];
    
    int main(){
            freopen("present.in", "r", stdin);
            freopen("present.out", "w", stdout);
            N = read(), M = read();
            for(reg int i = 1; i <= N; i ++) p[i] = read();
            for(reg int i = 1; i <= M; i ++) a[i] = read();
            memset(F, 0x3f, sizeof F);
            F[0] = 0;
            std::priority_queue <pr, std::vector<pr>, std::greater<pr> > Q;
            Q.push(pr(0, 0));
            while(!Q.empty()){
                    int ft = Q.top().se; Q.pop();
                    for(reg int i = 2; i <= N; i ++){
                            int to = ft + p[i], cur = to%p[1];
                            if(F[cur] > F[ft] + to/p[1]){
                                    F[cur] = F[ft] + to/p[1];
                                    Q.push(pr(F[cur], cur));
                            }
                    }
            }
            for(reg int i = 1; i <= M; i ++)
                    if(F[a[i]%p[1]] <= a[i]/p[1]) Ans ++;
            printf("%d
    ", Ans);
            return 0;
    }
    
  • 相关阅读:
    opencv-霍夫直线变换与圆变换
    opencv-角点检测之Harris角点检测
    opencv-图像形态学之开运算、闭运算、形态学梯度、顶帽、黑帽合辑
    opencv-图像形态学之膨胀腐蚀
    MFC-按行读取TXT数据
    MFC-CString与int互相转化
    转-C++宏定义详解
    转-pycharm建立项目
    转-Windows下anaconda简单使用教程
    CSU 1556 Jerry's trouble
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822500.html
Copyright © 2011-2022 走看看