• P3943 星空


    感谢 LBXLBX 赞助 链接

    星空


    Description

    链接
    给出一列长度为 NNKK 个位置未被点亮的灯泡, 允许反转 MM 种不同长度, 问最少反转多少次使所有灯泡亮起 .


    Solution

    异或差分:
    作用: 用来对 0,10, 1 数列进行多次区间反转
    具体步骤:

    1. 在原来 AA 数组的基础上建立 BB (异或差分) 数组 , Bi=Ai1Ai   .B_i = A_{i-1}igoplus A_i .(igoplus表异或)
    2. 对于一次操作 [L,R][L, R], 对 BL,BR+1B_L, B_{R+1} 分别进行异或操作
    3. 操作完成之后即可O(N)mathcal{O(N)}得到处理后的数列

    有了 前置芝士, 就可以来说说这道题目的解法了,

    设原来的灯泡序列为 0,10, 1 序列, 序列中 11 表示需要被点亮, 将其转换为 异或差分数组,

    目的 是将 全部元素 变为 00 . (此时必定没有熄灭的灯)

    消去 11 的方法只有异或, 即同时使 l,r+1l, r+1 位置的 “1” 消去 .

    为了避免浪费操作数, 要保证每次 区间反转 时的 l  r+1l 或 r+1 位置至少有 11 个 “1”,
    则操作时 l  r+1l 和 r+1 位置的值分 两种情况

    1. 11” 和 “00”, 操作时不会影响 11 的总个数, 但却可以看做是 “11” 挪动了位置.
    2. 11” 和 “11”, 操作时可以直接消去两个 “11”.

    2.操作 可以从本质上消去两个 “1”,
    为了使当前局面可以满足使用 2.操作 的条件, 可以设想使用 1.操作 使 “1” 移动到对应位置,
    此时又要 花费最小, 所以可以使用 SPFAmathcal{SPFA} 处理出每个 “1” 到各个位置上的 最少操作次数
    则消掉两个 ‘1’ 的代价即可直接使用 Dis[i,j]Dis[i, j] 表示出来 .


    接下来说怎么 计算答案 ,

    熄灭的灯泡只有不超过 88 个, 则 差分数组 中 ‘1’ 不会超过 1616 个,
    则直接暴力 DFSDFS 枚举哪两个灯泡匹配, 更新答案即可.
    时间复杂度O(91618)O(9^{16}*18).

    也可使用 状压dp ,

    时间复杂度 O(216182)O(2^{16}*18^2)


    Code

    #include<bits/stdc++.h>
    #define reg register
    
    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 K;
    int M;
    int cnt;
    int A[40004];
    int B[40004];
    int pos[205];
    int opt[205];
    int Dis[18][40004];
    int dp[(1<<18) + 233];
    
    std::bitset <40004> Used;
    
    void SPFA(const int &x){
            std::queue <int> Q;
            Q.push(pos[x]);
            Used.set(pos[x], 1), Dis[x][pos[x]] = 0;
            while(!Q.empty()){
                    int ft = Q.front(); Q.pop();
                    Used.set(ft, 0); int tmp;
                    for(reg int i = 1; i <= M; i ++){
                            tmp = ft - opt[i]; 
                            if(tmp > 1 && Dis[x][tmp] > Dis[x][ft] + 1){ 
                                    Dis[x][tmp] = Dis[x][ft] + 1; 
                                    if(!Used.test(tmp)) Q.push(tmp), Used.set(tmp, 1); 
                            }
                            tmp = ft + opt[i];
                            if(tmp <= N+1 && Dis[x][tmp] > Dis[x][ft] + 1){
                                    Dis[x][tmp] = Dis[x][ft] + 1;
                                    if(!Used.test(tmp)) Q.push(tmp), Used.set(tmp, 1); 
                            }
                    }
            }
    }
    
    int main(){
            freopen("starlit.in", "r", stdin);
            freopen("starlit.out", "w", stdout);
            N = read(), K = read(), M = read();
            for(reg int i = 1; i <= K; i ++) A[read()] = 1;
            for(reg int i = 1; i <= M; i ++) opt[i] = read();
            for(reg int i = 1; i <= N+1; i ++){
                    B[i] = A[i]^A[i-1];
                    if(B[i]) pos[++ cnt] = i;
            }
            memset(Dis, 0x3f, sizeof Dis);
            for(reg int i = 1; i <= cnt; i ++) SPFA(i);
            memset(dp, 0x3f, sizeof dp);
            dp[0] = 0;
            for(reg int i = 0; i <= (1<<cnt)-1; i ++)
                    for(reg int l = 1; l <= cnt; l ++)
                            for(reg int r = l+1; r <= cnt; r ++){
                                    if(!((i>>cnt-l)&1) || !((i>>cnt-r)&1)) continue ;
                                    int tmp = i ^ (1<<cnt-l) ^ (1<<cnt-r);
                                    dp[i] = std::min(dp[i], dp[tmp]+Dis[l][pos[r]]);
                            }
            printf("%d
    ", dp[(1<<cnt)-1]);
            return 0;
    }
    

    这里有一道类似的题

  • 相关阅读:
    深入理解java虚拟机笔记Chapter12
    深入理解java虚拟机笔记Chapter11
    深入理解java虚拟机笔记Chapter8
    深入理解java虚拟机笔记Chapter7
    深入理解java虚拟机笔记补充-JVM常见参数设置
    深入理解java虚拟机笔记Chapter4
    深入理解java虚拟机笔记Chapter3-内存分配策略
    Java从Txt文本进行数据提取
    C语言-格式输入输出中“%d,%o,%x,%e,%f,%s,%p”
    嵌入式学习书籍
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822634.html
Copyright © 2020-2023  润新知