1. 程式人生 > 實用技巧 >狀壓dp--P3943 星空

狀壓dp--P3943 星空

一道比較綜合的題

一些想法:

  1. 區間狀態取反:想到異或和差分

  2. \(k\leq8\),那麼最多會有16個1,\(2^{16}\),想到狀態壓縮

  3. 給出\(m\)個區間長度,由他們組合出的區間長度的最小操作次數\(dp\)搞一下

  4. 每次操作會把一對1取反,把\(2^{16}\)種狀態依次把所有操作執行一遍,與得到的狀態連邊

  5. 求初始狀態和末狀態的最短路

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define N maxn


using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 1e6+10,inf = 1e9+7;
queue<int>q;
int n,k,m;
int a[maxn],sum[maxn],s[maxn],cnt,arr[maxn];
int b[maxn],vis[maxn],f[N];
struct node{
    int to,nxt,w;
}ed[maxn*20];
int head[maxn],tot;
void add(int u,int to,int w){
    ed[++tot].to = to;
    ed[tot].w = w;
    ed[tot].nxt = head[u];
    head[u] = tot;
}
int dis[maxn],vis1[maxn];
void SPFA(int s){
    queue<int> q1;q1.push(s);
    for (int i = 0;i < (1<<cnt);i++) dis[i] = inf;
    dis[s] = 0;vis1[s] = 1;
    while (!q1.empty()){
        int x = q1.front();q1.pop();
        vis[x] = 0;
        for (int i = head[x];i;i = ed[i].nxt){
            int to = ed[i].to;
            if (dis[to] > dis[x] + ed[i].w){
                dis[to] = dis[x] + ed[i].w;
                if (!vis1[to]){
                    vis1[to] = 1;
                    q1.push(to);
                }
            }
        }
    }
}
int main(){
	n = read(),k = read(),m = read();
	for (int i = 0;i <= n+1;i++) a[i] = 1;
	for (int i = 1;i <= k;i++){
		int x = read();a[x] = 0;
	}
	for (int i = 1;i <= n+1;i++) sum[i] = a[i]^a[i-1];
	for (int i = 1;i <= m;i++) b[i] = read();
	for (int i = 1;i <= n+1;i++){
		if (sum[i] == 1){
			arr[++cnt] = 1;
			s[cnt] = i;
		}
	}
	for (int i = 1;i <= n;i++) f[i] = inf;
	f[0] = 0;q.push(0);vis[0] = 1;
	while(!q.empty()) {
		int u = q.front();q.pop();
		for(int i = 1;i <= m;i ++) {
			if(u+b[i] <= n && vis[u+b[i]] == 0) {
				vis[u+b[i]] = 1,f[u+b[i]] = f[u] + 1;
				q.push(u+b[i]);
			}
			if(u-b[i] > 0 && vis[u-b[i]] == 0) {
				vis[u-b[i]] = 1,f[u-b[i]] = f[u] + 1;
				q.push(u-b[i]);
			}
		}
	}
	for (int k = 0;k < (1<<cnt);k++){
    	for (int i = 1;i <= cnt;i++){
    		for (int j = i+1;j <= cnt;j++){
                int res = k;
    			int val = f[s[j]-s[i]];
                res ^= (1<<i>>1),res ^= (1<<j>>1);
                add(k,res,val);
    		}
    	}
    }
    int pos = (1<<cnt)-1;
    SPFA(pos);
    printf("%d\n",dis[0]);
	return 0;
}