码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P3694 邦邦的大合唱站队 【状压dp】By cellur925

时间:2018-10-02 14:07:53      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:col   转移   eof   using   状压dp   为我   不能   div   注意   

题目传送门

最开始学状压的时候...学长就讲的是这个题。当时对于刚好像明白互不侵犯和炮兵阵地的我来说好像在听天书......。因为我当时心里想,这又不是什么棋盘,咋状压啊?!后来发现这样的状压多了去了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\n",f[fAKe]);
34     return 0;
35 }
View Code

 

Luogu P3694 邦邦的大合唱站队 【状压dp】By cellur925

标签:col   转移   eof   using   状压dp   为我   不能   div   注意   

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9736340.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!