zoukankan      html  css  js  c++  java
  • Luogu P3694 邦邦的大合唱站队 【状压dp】By cellur925

    题目传送门

    最开始学状压的时候...学长就讲的是这个题。当时对于刚好像明白互不侵犯和炮兵阵地的我来说好像在听天书......。因为我当时心里想,这又不是什么棋盘,咋状压啊?!后来发现这样的状压多了去了hhh。后来这道题就一直压着了,现在对状压明白了一点便来填坑。


    我们注意到,团体队员数$N$比较大,而团体数$M$很小(不能称为乐队)。那么我们可以在$m$上下功夫,把它压成二进制串。开始想的状态是0表示这个团体还没站好,1表示这个团体已经站好了。看了看jtdalao的文章发现自己的状态是对的,但是转移嘛...,有点迷感觉。一步一步来。首先我们肯定要枚举当前的状态是什么,按照状压dp的套路,我们接下来要枚举下这一次新站好的是哪个队。转移时我们需要用在新队站好前的状态所需的次数+站成新队另需要的人数。我们默认大家都是排成一列紧跟在一个人之后的,所以我们需要求出当前已经排到谁了。所以我们还需要开一个数组前缀和来记录排队的信息。设$sum[i][j]$表示到$i$位置,$j$团体的人数。

    转移有:

    $f[i]$=$min$$($f[i^(1<<j)]$+$num[j]$-($sum[pos][j]$-$sum[pos-num[j]][j]$))。

    因为当前新加的段之前也可能有本队队员,所以把他们减去。

    初值,因为求最小,所以开始赋成极大,$f[0]=0$。(边界)

    之后就比较好想了==。


    $Code$

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 
     5 using namespace std;
     6 
     7 int n,m,fAKe;
     8 int f[1<<23],num[50],sum[100090][25];
     9 
    10 int main()
    11 {
    12     scanf("%d%d",&n,&m);
    13     for(int i=1;i<=n;i++)
    14     {
    15         int x=0;
    16         scanf("%d",&x);
    17         num[x-1]++;
    18         sum[i][x-1]++;
    19         for(int j=0;j<m;j++)
    20             sum[i][j]+=sum[i-1][j];
    21     }
    22     fAKe=(1<<m)-1;
    23     memset(f,0x3f,sizeof(f));
    24     f[0]=0;
    25     for(int i=0;i<=fAKe;i++)
    26     {
    27         int pos=0;
    28         for(int j=0;j<m;j++)
    29             if(i&(1<<j)) pos+=num[j];
    30         for(int j=0;j<m;j++)
    31             f[i]=min(f[i],f[i^(1<<j)]+num[j]-(sum[pos][j]-sum[pos-num[j]][j]));
    32     }
    33     printf("%d
    ",f[fAKe]);
    34     return 0;
    35 }
    View Code
  • 相关阅读:
    Hbase­优化方案
    ssh 登录
    微软2017校招笔试题3 registration day
    微软2017校招笔试题2 composition
    STL中的查找算法
    leetcode-188 买卖股票4
    leetcode-306 Additive Number
    网络安全(3): 数据完整性校验
    网络安全(2)-数据加解密
    linux共享库
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9736340.html
Copyright © 2011-2022 走看看