【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 */