[模擬考試題][神題]Star Sky[狀壓DP][BFS][差分]
阿新 • • 發佈:2019-01-24
題意:
給你n個排成一排的燈,0代表開著,1代表關著,有k盞開著的燈。現在你有m種不同長度的電線,可以使得leni那麼長的區間反轉(0變1,1變0)。現在問你使得整個區間的燈全部開著的最少的運算元。
N <= 40000 K <= 8 M <= 64
真心神題。首先我們觀察到序列上的操作都是對於一整段區間而言的,自然想到差分(自然個鬼啊!!),我們將序列用異或差分,然後對一個區間的取反操作變成了對端點的取反。滿足條件的序列是全為0的。k很小,就想到在k上做文章。
分析差分序列上的操作:
1.前面有一個1後面有一個0:分別取反變成1 0,相當於1移到了0的位置上。
2.前1後1:將他們同時取反就可以接近目標序列
取反操作的前提是兩個bit位相差的距離存在於m個len中,但是同樣的,多次移動操作可以構造出合法距離來消掉1
所以想到以每一個差分序列上的1為起點,跑BFS處理它到達其他的1的最小運算元(到達了便消除了)
O(nm)建邊
然後用狀壓DP找到消除1的最優操作順序
真心神題。模型轉化太強了。
首先你得把區間操作轉化為差分,並且要想到最終狀態給我們的啟示——消掉1,然後還要想到用BFS處理距離,然後還要想到用狀壓處理操作順序。接近省選題了吧。真心好題。%%%%%出題人
總複雜度O(nm + nmk + k * 2 ^ 2k) (狀壓沒有處理好我T了4個點至今未解決)
#include <bits/stdc++.h> using namespace std; const int N = 40005; int n, k, m, val[1 << 17], a[N], b[N], len[N], head[N], top, dis[20][N], pos[20], f[1 << 17]; struct Node { int y, nxt; Node() { } Node( int y, int nxt ) : y(y), nxt(nxt) { } } e[N * 129]; void Adde( int x, int y ) { e[++top] = Node(y, head[x]), head[x] = top; e[++top] = Node(x, head[y]), head[y] = top; } int h, t, que[N]; void Bfs( int st, int num ) { h = t = 0; memset(dis[num], 0x3f, sizeof(dis[num])); dis[num][st] = 0; que[++t] = st; while(h < t) { int u = que[++h]; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].y; if(dis[num][v] != 0x3f3f3f3f) continue; dis[num][v] = dis[num][u] + 1; que[++t] = v; } } } int Dfs( int sta ) { if(sta == 0) return 0; if(f[sta] != -1) return f[sta]; f[sta] = 0x3f3f3f3f; int tmp = sta - ((sta) & (-sta)), cur = tmp, num1 = val[sta & (-sta)]; while(cur) { if(dis[num1][pos[val[cur & (-cur)]]] != 0x3f3f3f3f) f[sta] = min(f[sta], Dfs(tmp - (cur & (-cur))) + dis[num1][pos[val[cur & (-cur)]]]); cur -= cur & (-cur); } return f[sta]; } int main() { cin >> n >> k >> m; for(int i = 1; i <= 16; ++i) val[1 << (i - 1)] = i; for(int i = 1, x; i <= k; ++i) scanf( "%d", &x ), a[x] = 1; for(int i = 1; i <= m; ++i) scanf( "%d", &len[i] ); for(int i = 0; i <= n; ++i) { b[i] = a[i] ^ a[i + 1]; for(int j = 1; j <= m; ++j) if(i + len[j] <= n) Adde(i, i + len[j]); } int cnt = 0, sit = 0; for(int i = 0; i <= n; ++i) if(b[i]) Bfs(i, ++cnt), pos[cnt] = i; memset(f, -1, sizeof(f)); sit = (1 << cnt) - 1; printf( "%d\n", Dfs(sit) ); return 0; } /* 5 2 2 1 5 3 4 */