题解:
\(F[p][s]\)表示讨论到p号点,根到p路径(不包含p)上的点的服务状态为s的最小费用。
\(S\)是\(3\)进制数,\(0\)表示\(p\)自己服务自己,\(1\)表示\(p\)没有被服务到,\(2\)表示被其它点服务,这里的状态记录的是父亲
一直到根这条链上的,不超过\(10\)层,所以最多是\(3^{10}\)
\(code\):
//待补
题解:
\(bfs\)算出置反一段区间的最小代价,状压更新
\(code:\)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctype.h>
#include<queue>
#define inf 1e9+9
using namespace std;
int t,n,k,l,cnt,tot;
int a[10005],g[10005],pos[10005],c[10005],f[1<<21];
queue<int>q;
int main()
{
// freopen("1.txt","w",stdout);
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&k,&l);cnt=0;
for(int i=1;i<=k;i++) scanf("%d",&a[i]);
for(int i=1;i<=l;i++) scanf("%d",&c[i]),q.push(c[i]);
sort(a+1,a+k+1);
k=unique(a+1,a+k+1)-(a+1);
a[0]=-1,a[k+1]=n+2;
for(int i=1; i<=k; i++) {
if(a[i]!=a[i-1]+1) pos[cnt++]=a[i];
if(a[i]!=a[i+1]-1) pos[cnt++]=a[i]+1;
}tot=(1<<cnt)-1;
// for(int i=0;i<cnt;i++) printf("%d:%d\n",i+1,pos[i]);
sort(pos,pos+cnt);
for(int i=1;i<=n;i++) g[i]=inf;
for(int i=1;i<=l;i++) g[c[i]]=1;
// for(int i=0;i<cnt;i++) printf("%d:%d\n",i+1,pos[i]);
while(!q.empty()) {
int x=q.front();
q.pop();
for(int i=1; i<=l; i++) {
if(x+c[i]<=n&&g[x+c[i]]==inf) {
g[x+c[i]]=g[x]+1;
q.push(x+c[i]);
}
if(x-c[i]>=1&&g[x-c[i]]==inf) {
g[x-c[i]]=g[x]+1;
q.push(x-c[i]);
}
}
}
// for(int i=1;i<=n;i++) printf("%d\n",g[i]);
for(int s=1;s<=tot;s++)
{
f[s]=inf;int i;
for(i=0;i<cnt;i++)
if(s>>i&1) break;
for(int j=i+1;j<cnt;j++) if(s>>j&1)
f[s]=min(f[s],f[s^(1<<i)^(1<<j)]+g[pos[j]-pos[i]]);
}
// for(int i=0;i<cnt;i++) printf("%d:%d\n",i+1,pos[i]);
if(f[tot]==inf) puts("-1");
else printf("%d\n",f[tot]);
}
}