您的位置:首页 > 其它

[置顶] [模拟考试题][神题]Star Sky[状压DP][BFS][差分]

2017-10-24 21:58 369 查看
题意:

给你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
, b
, len
, head
, top, dis[20]
, 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
;

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
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: