【BZOJ3003】差分+BFS+状压DP
orz orz orz orz
BZOJ3003(权限)
3003: LED
Time Limit: 20 Sec Memory Limit: 128 MB Submit: 159 Solved: 40 [Submit][Status][Discuss]Description
LED屏是由一个庞大的点阵小灯泡组成的,一开始每个小灯泡都不发光。每一行一共有N个小灯泡,依次标号为1~n。现在给定K个点,要求这K个点发光,其余点必须保持熄灭状态。而这块LED屏的操作方式各种奇葩,一共有L种操作方法,第i种表示你能将任意长度恰为A_i的连续一段灯泡的状态取反(灭变亮,亮变灭)。
已知LED屏一共有m行,为了节省时间,请你算出每一行达到目标状态所需的最少操作次数。
Input
输入文件第一行一个数m,表示LED屏的行数。
对于LED屏的每一行:
第一行为n,k,l,意义见上。
第二行为k个数,表示要求发光的k个点。
第三行为l个数,表示l种操作方式。
Output
对于LED屏的每一行:如果无法达到目标状态,输出-1,否则输出最少次数。
Sample Input
2
10 8 2
1 2 3 5 6 7 8 9
3 5
3 2 1
1 2
3
Sample Output
2
-1
【数据规模】
对于100%的数据,T≤10,N≤10000,K≤10,L≤100,1≤A_i≤N。
HINT
Source
对于这道题首先要将原图转化-->我们将原图差分一下(这个朴素,但是十分有用的操作就是本题的关键),那么对于区间整体的异或,就可以转变为转化两点了,同样这样我们将原题恶心的区间修改这K个点,变成了每次修改两个点,然后将这2K个点消除。 由于K很小。对于从一个点作为端点出发可以修改的点和修改的最小代价,我们可以bfs一下,然后转化为状压背包就搞定了。/*
先将数组差分,然后跑一个背包得到一个距离所需最小的,
之后bfs优化一下DP就可以了
dp[S]将集合中的所有消掉的最小代价
*/
#include<stdio.h>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int n,k,L;
const int maxn = 10005;
bool gg[maxn];
bool cf[maxn];
vector<int>ve;
int all;
int wp[maxn];
int f[maxn];
int dis[25][25];
int dp[1<<20];
queue<int>q;
void bfs(int ow) {
memset(f,0x3f,sizeof f);
while(q.size()) q.pop();
q.push(ve[ow]); f[ve[ow]] = 0;
while(q.size()) {
int x = q.front(); q.pop();
for(int i=1;i<=L;i++) {
if(x+wp[i]<=n+1)
{
if(f[x+wp[i]]>=0x3f3f3f3f) {
f[x+wp[i]] = f[x] + 1;
q.push(x+wp[i]);
}
}
if(x-wp[i]>=1)
{
if(f[x-wp[i]]>=0x3f3f3f3f) {
f[x-wp[i]] = f[x] + 1;
q.push(x-wp[i]);
}
}
}
}
for(int i=0;i<all;i++) {
if(i!=ow) {
dis[ow][i] = f[ve[i]];
}
}
}
int logg[1<<20];
void solve() {
memset(gg,0,sizeof gg); memset(cf,0,sizeof cf);
ve.clear();
all = 0;
scanf("%d%d%d",&n,&k,&L);
for(int i=1;i<=k;i++) {
int x; scanf("%d",&x);
gg[x]=1;
}
for(int i=1;i<=n+1;i++) {
cf[i]=(gg[i]!=gg[i-1]);
if(cf[i]) {
all++; ve.push_back(i);
}
}
memset(dp,0x3f,sizeof dp); dp[0]=0;
for(int i=1;i<=L;i++) scanf("%d",&wp[i]);
sort(wp+1,wp+1+L);
L = unique(wp+1,wp+1+L)-wp-1;
for(int i=0;i<all;i++) bfs(i);
for(int i=3;i<(1<<all);i++) {
int x = logg[i&(-i)];
for(int y=x+1;y<all;y++) {
if(!(i>>y)&1) continue;
dp[i] = min(dp[i],dp[i^(1<<x)^(1<<y)]+dis[x][y]);
}
}
if(dp[(1<<all)-1]<0x3f3f3f3f) printf("%d\n",dp[(1<<all)-1]);
else printf("-1\n");
}
int main() {
int T; scanf("%d",&T);
for(int i=0;i<20;i++) {
logg[(1<<i)] = i;
}
while(T--) solve();
}
/*
1
10 8 1
1 2 3 5 6 7 8 9
3
*/

浙公网安备 33010602011771号