折半+dp之限制转状态+状压:CF1767E
本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/133607589
https://vjudge.net/problem/CodeForces-1767E/origin
首先40,必然折半。然后怎么做?
分析性质。每次可以走1步or2步,等价什么?等价任意相邻2个必选一个!然后就可以建图
这个图是个限制图,我们折半后可以进行状压。dp的过程是限制转状态。
首先分别的,前后内部都必须满足。然后对于交织在两部分的限制,我们枚举其中一边哪些不选,必然可以对应另外那边哪些必选。得到的集合求其最小合法超集即是答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #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 #define N 300010 #define M 21 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) { if (b[i][j] && !(s&(1 <<i-mid))) t|=(1 <<j); } } signed main () { n=read (); m=read (); mid=m/2 ; ans=1e18 ; 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); 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)) { for (i=k=0 ; i<mid; ++i) if (s&(1 <<i)) k+=cost[i]; 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)) { t=0 ; Least (s, t); for (i=k=0 ; i<m-mid; ++i) if (s&(1 <<i)) k+=cost[i+mid]; ans=min (ans, k+f[t]); } } printf ("%lld" , ans); return 0 ; }