[loj3302]信号传递

由于n较大,可以将n个数中的关系对数量记录在$m*m$的矩阵中,记作$a[i][j]$
考虑朴素的状压dp枚举排列,即$f[i]$表示以i中的数的一种排列为整个序列的前缀的最小代价,然后转移枚举下一个数j以及与其相关的数k,那么有转移$f[i|j]=min(f[i]+(|i|+1)(sum_{kin i}limits (tcdot a[j][k]+a[k][j])+sum_{k otin i}limits (tcdot a[k][j]-a[j][k])))$,时间复杂度为$o(m^{2}cdot 2^{m})$
对时间优化,对于一个i,需要快速求出下一个数为j时$|i|+1$的系数,设为$g[i][j]$,考虑从$i-lowbit(i)$转移,那么有转移$g[i][j]=g[i-k][j]+tcdot a[j][k]+a[k][j]-(tcdot a[k][j]-a[j][k])$,其中$k=lowbit(i)$,时间复杂度降为$o(mcdot 2^{m})$,但空间复杂度变为$o(mcdot 2^{m})$
对空间优化,有一个十分巧妙的做法:考虑g中修改1位的复杂度为$o(m)$,同时由于对0不断高精度+1加到$2^m$复杂度均摊$o(1)$,那么从小到大枚举i并对g暴力修改即可,时间复杂度不变,空间复杂度降为$o(2^{m}+m)$
 
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,t,x,y,a[105][105],f[10000005],g[105];
 4 int main(){
 5     scanf("%d%d%d",&m,&n,&t);
 6     for(int i=1;i<=m;i++){
 7         scanf("%d",&y);
 8         y--;
 9         if (i>1)a[x][y]++;
10         x=y;
11     }
12     for(int i=0;i<n;i++)a[i][i]=0;
13     memset(f,0x3f,sizeof(f));
14     f[0]=0;
15     for(int i=0;i<(1<<n);i++){
16         int s=1,l=i-(i&(i-1));
17         for(int j=0;j<n;j++)s+=((i&(1<<j))>0);
18         for(int j=0;(1<<j)<=l;j++)
19             for(int k=0;k<n;k++)
20                 g[k]=g[k]+(t*a[k][j]+a[j][k]+a[k][j]-t*a[j][k])*(1-2*((1<<j)<l));
21         if (!l)l=(1<<n);
22         for(int j=0;(1<<j)<=l;j++){
23             g[j]=0;
24             if ((1<<j)<l)
25                 for(int k=0;k<n;k++)
26                     if ((i&(1<<k))==0)g[j]+=t*a[k][j]-a[j][k];
27                     else g[j]+=t*a[j][k]+a[k][j];
28         }
29         for(int j=0;j<n;j++)
30             if ((i&(1<<j))==0)f[i|(1<<j)]=min(f[i|(1<<j)],f[i]+s*g[j]);
31     }
32     printf("%d",f[(1<<n)-1]);
33 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/13297312.html