https://vjudge.net/problem/CodeForces-1767E/origin
首先40,必然折半。然后怎么做?
分析性质。每次可以走1步or2步,等价什么?等价任意相邻2个必选一个!然后就可以建图
这个图是个限制图,我们折半后可以进行状压。dp的过程是限制转状态。
首先分别的,前后内部都必须满足。然后对于交织在两部分的限制,我们枚举其中一边哪些不选,必然可以对应另外那边哪些必选。得到的集合求其最小合法超集即是答案。
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 300010
#define M 21
//#define mo
int n, m, i, j, k, T;
int ans, f[1<<M], b[M<<1][M<<1];
int a[N], s, t, mid, cost[M<<1];
int k1, k2, s1, s2;
void cun(int x, int y) {
b[x][y]=b[y][x]=1;
}
int check(int s) {
if(k1<mid && (s&s1)==0) return 0;
if(k2<mid && (s&s2)==0) return 0;
for(int i=0; i<mid; ++i)
for(int j=0; j<mid; ++j)
if(b[i][j] && (!(s&(1<<i)) && !(s&(1<<j)))) return 0;
return 1;
}
int check2(int s) {
if(k1>=mid && (s&s1)==0) return 0;
if(k2>=mid && (s&s2)==0) return 0;
for(int i=mid; i<m; ++i)
for(int j=mid; j<m; ++j)
if(b[i][j] && (!(s&(1<<i-mid)) && !(s&(1<<j-mid)))) return 0;
return 1;
}
void Least(int s, int &t) {
for(int i=mid; i<m; ++i)
for(int j=0; j<mid; ++j) {
// printf("(%d %d) %d\n", i, j);
if(b[i][j] && !(s&(1<<i-mid))) t|=(1<<j);
}
}
signed main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// T=read();
// while(T--) {
//
// }
n=read(); m=read(); mid=m/2; ans=1e18;
// printf("%lld\n", mid);
for(i=1; i<=n; ++i) a[i]=read()-1;
s1=(1<<a[1]); s2=(1<<a[n]);
k1=a[1]; k2=a[n];
if(k1>=mid) s1=(1<<a[1]-mid);
if(k2>=mid) s2=(1<<a[n]-mid);
// printf("%d %d | %d %d\n", k1, k2, s1, s2);
for(i=1; i<n; ++i) cun(a[i], a[i+1]);
for(i=0; i<m; ++i) cost[i]=read();
memset(f, 0x3f, sizeof(f));
for(s=(1<<mid)-1; s>=0; --s) {
if(check(s)) {
// printf("> %d ", s);
for(i=k=0; i<mid; ++i) if(s&(1<<i)) k+=cost[i];
// printf("%lld\n", k);
f[s]=min(f[s], k);
}
for(i=0; i<mid; ++i) if(s&(1<<i))
f[s-(1<<i)]=min(f[s-(1<<i)], f[s]);
}
for(s=0; s<(1<<m-mid); ++s) {
if(check2(s)) {
// printf(">> %d ", s);
t=0; Least(s, t);
for(i=k=0; i<m-mid; ++i) if(s&(1<<i)) k+=cost[i+mid];
// printf("%lld %lld\n", k, t);
ans=min(ans, k+f[t]);
}
}
printf("%lld", ans);
return 0;
}