一眼求最优方案,DP
一眼
m
m
m巨小,状压
一眼状压DP,不会
集合为
S
S
S,
f
[
S
]
f[S]
f[S]为答案,
g
[
i
]
[
S
]
g[i][S]
g[i][S]为加入
i
i
i对点集
S
S
S的贡献
有转移方程:
f
[
S
+
i
]
=
min
(
f
[
S
]
+
g
[
i
]
[
S
]
)
f[S+i]=\min(f[S]+g[i][S])
f[S+i]=min(f[S]+g[i][S])
暴力枚举
O
(
m
2
2
m
)
O(m^22^m)
O(m22m)炸裂
考虑优化:
预处理优化
O
(
m
2
m
)
O(m2^m)
O(m2m),但空间
O
(
m
2
m
)
O(m2^m)
O(m2m),空间炸裂
考虑坎空间
按照二进制位枚数
需要理解lowbit
函数的意义:二进制下最低的1
位的数值
用一个val
来记录每个点承受的费用,向右走就-1
,向左走就*k
(如:
x
→
y
,
x
≤
y
x\to y,x\le y
x→y,x≤y,给
x
x
x累加代价
−
1
×
x
-1\times x
−1×x,给y累加代价
y
y
y)
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
#define lowbit(x) (x)&(-(x))
const int N=2e5+5;
const int INF=1e9;
const int M=24;
int n,m,k,sz[1<<M],to[N],id[1<<M],rd[M][M],f[1<<M];
struct Cost{
int pn,val[M];
}p1,p2;
queue<Cost>q;
int main(){
n=in,m=in,k=in;
for(int i=1;i<=n;++i) to[i]=in-1;
for(int i=1;i<n;++i) ++rd[to[i]][to[i+1]];
p1.pn=0;
for(int i=0;i<m;++i){
p1.val[i]=0;
for(int j=0;j<m;++j){
if(i==j) continue;
p1.val[i]-=rd[i][j];
p1.val[i]+=k*rd[j][i];
}
}
q.push(p1);
int tot=(1<<m)-1;
for(int i=1;i<=tot;++i){
f[i]=INF;
sz[i]=sz[i>>1]+(i&1);
}
for(int i=1;i<=m;++i) id[(1<<i)]=i;
while(!q.empty()){
p1=q.front();q.pop();
int siz=sz[p1.pn]+1;
for(int i=tot^p1.pn;i;i^=lowbit(i)){
int j=id[lowbit(i)];
int tmp=f[p1.pn]+siz*p1.val[j];
int sta=(p1.pn|(1<<j));
f[sta]=min(f[sta],tmp);
}
for(int i=0;i<m;++i){
if(p1.pn&(1<<i)) break;
p2.pn=(p1.pn|(1<<i));
for(int j=tot^p2.pn;j;j^=lowbit(j)){
int r=id[lowbit(j)];
p2.val[r]=p1.val[r]+(k*rd[r][i]+rd[i][r])-(-rd[r][i]+k*rd[i][r]);
}
q.push(p2);
}
}
printf("%d\n",f[tot]);
return 0;
}