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

[状压DP]luogu P6622 [省选联考 2020 A/B 卷] 信号传递

时间:2021-03-29 12:31:50      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:gif   can   瓶颈   ide   one   状态   open   time   inf   

题面

https://www.luogu.com.cn/problem/P6622

分析

枚举每个信号塔的位置显然不行,考虑设置 DP 状态

f[S] 表示选择了集合为 S 的塔,排在前 |S| 个位置

方程则为 $f[S|i]=f[S]+h[S,i]$ $h[S,i]$ 表示 S 中与 i 有连边的贡献

$h[S,i]$ 怎么求呢?据题意有

$w_{i,j}=j-i (i<j)$

$w_{i,j}=ki+kj (i>j)$

发现可以对于每个位置来计算贡献,有一条向后的出边则 -1 ,向前的出边则 +k ,从后来的入边则 +k ,从前来的入边则 +1

设 $c_{i,j}$ 为 i 到 j 的边数,则有 $h[S,i]=(|S|+1)\times(\sum_{j\in S}(kc_{i,j}+c_{j,i}) + \sum_{j\notin S|i}(kc_{j,i}+c_{i,j}))$

这样时间复杂度是 $O(m^22^m)$ 的,如果从低位往高位枚举 S 并同时转移 h[S,i] ,则可以压到 $O(m2^m)$

但是空间复杂度仍然不足以AC,瓶颈在于 h[S,i] 

发现如果从小往大枚举,那么最近枚举的一个位数为 i 的状态 S 枚举到下一个位数为 i 的状态 S‘ 之前一定会枚举到一个位数为 i+1 的状态 S‘‘ ,而 S‘‘ 显然为 S+lowbit(S)

所以可以直接改为 h[|S|,i] ,每次转移的时候从 h[|S|-1,i] 加上对应贡献即可

代码

技术图片
#include <iostream>
#include <cstdio>
#define lowbit(x) (x&-x)
using namespace std;
const int Inf=2147483647;
const int N=1e5+1;
const int M=23;
int n,m,k;
int g[M][M],f[1<<M],h[M][M],cnt[1<<M],lg[1<<M];

int main() {
    scanf("%d%d%d",&n,&m,&k);
    int u;scanf("%d",&u);
    for (int i=1,v;i<n;i++) scanf("%d",&v),g[u-1][v-1]++,u=v;
    for (int i=0;i<m;i++)
        for (int j=0;j<m;j++)
            if (i!=j) h[0][i]+=k*g[j][i]-g[i][j];
    n=1<<m;lg[0]=-1;for (int i=1;i<n;i++) lg[i]=lg[i>>1]+1,cnt[i]=cnt[i-lowbit(i)]+1,f[i]=Inf;
    for (int i=0;i<n-1;i++) {
        if (i) for (int j=0;j<m;j++) h[cnt[i]][j]=h[cnt[i]-1][j]-k*g[lg[lowbit(i)]][j]+k*g[j][lg[lowbit(i)]]+g[j][lg[lowbit(i)]]+g[lg[lowbit(i)]][j];
        for (int j=(n-1)^i;lowbit(j);j-=lowbit(j)) f[i|lowbit(j)]=min(f[i|lowbit(j)],f[i]+h[cnt[i]][lg[lowbit(j)]]*(cnt[i]+1));
    }
    printf("%d\n",f[n-1]);
}
View Code

 

[状压DP]luogu P6622 [省选联考 2020 A/B 卷] 信号传递

标签:gif   can   瓶颈   ide   one   状态   open   time   inf   

原文地址:https://www.cnblogs.com/mastervan/p/14586153.html

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