您的位置:首页 > 其它

BZOJ(本校) 2665 密码锁 - 思维&dp

2016-03-07 16:53 197 查看
Time Limit: 1s

Memory Limit: 512MB

【题目描述】

从前有一把密码锁,由N个开关组成。一开始的时候,所有开关都是关上的。当且仅当开关x1,x2,x3,…xk为开,其他开关为关时,密码锁才会打开。

你可以进行M种的操作,每种操作有一个size[i],表示,假如你选择了第i种的操作的话,你可以任意选择连续的size[i]个格子,把它们全部取反。

你的任务很简单,最少需要多少步才能打开密码锁,或者如果无解的话,请输出-1。

【输入格式】

第1行,三个正整数N,K,M,如题目所述。

第2行,K个正整数,表示开关x1,x2,x3..xk必须为开,保证x两两不同。

第三行,M个正整数,表示size[i],size[]可能有重复元素。

【输出格式】

输出答案,无解输出-1。

【样例输入1】

10 8 2

1 2 3 5 6 7 8 9

3 5

【样例输出1】

2

【样例输入2】

3 2 1

1 2

3

【样例输出2】

-1

【数据规模与约定】

对于50%的数据,1≤N≤20,1≤k≤5,1≤m≤3;

对于另外20%的数据,1≤N≤10000,1≤k≤5,1≤m≤30;

对于100%的数据,1≤N≤10000,1≤k≤10,1≤m≤100。

分析:

因为取反操作时连续一段的,将连续一段的区间问题转化为类似差分的东西。

在连续一段都是1的开始位置设1,(结尾位置+1)的位置设1

原问题<=>将所有点两两配对(费用是一个点通过各个size的操作走到另一个点的最少操作次数),且费用和最小。(费用的处理用bfs搞最短路)

注意到k的范围很小[1,10],所以这样的点不超过20个,想到状压dp(s为一个20位的2进制数,表示要处理的点的集合):

dp[s]=min(dp[s], dp[s-(1<< i)-(1<< j)]+dist(i,j) ),i是新加入集合的点(即拿来配对的点),j是s中有的除i以外的点。

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cassert>
using namespace std;
#define MAXN 10000
#define MAXK 10
#define MAXM 100
#define MAXST 1048576
#define INF 2000000000

int n,k,m,light[MAXN+10],siz[MAXM+10],id[MAXN+10],edge[MAXK*2+10][MAXK*2+10],cntp;
int f[MAXST+10],dist[MAXN+10],g[MAXST+10];
bool vis[MAXN+10];

void read()
{
int x;
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=k;i++){
scanf("%d",&x);
light[x]=1;
}
for(int i=n+1;i>=1;i--)
light[i]^=light[i-1];
for(int i=1;i<=n+1;i++)
if(light[i])
id[i]=++cntp;
for(int i=1;i<=m;i++)
scanf("%d",&siz[i]);
sort(siz+1,siz+m+1);
m=unique(siz+1,siz+m+1)-(siz+1);
}
void Bfs(int s)
{
int u;
for(int i=1;i<=n+1;i++)
dist[i]=INF,vis[i]=false;
queue<int>  que;
que.push(s);
vis[s]=true,dist[s]=0;
while(!que.empty()){
u=que.front(); que.pop();
for(int i=1;i<=m;i++)
if(u+siz[i]<=n+1&&!vis[u+siz[i]]){
vis[u+siz[i]]=true;
dist[u+siz[i]]=dist[u]+1;
que.push(u+siz[i]);
}
for(int i=1;i<=m;i++)
if(u-siz[i]>=1&&!vis[u-siz[i]]){
vis[u-siz[i]]=true;
dist[u-siz[i]]=dist[u]+1;
que.push(u-siz[i]);
}
}
for(int i=1;i<=n+1;i++){
if(!light[i]) continue;
if(vis[i])
edge[id[s]][id[i]]=dist[i];
else
edge[id[s]][id[i]]=INF;
}
}
void Getdist()
{
for(int i=1;i<=n+1;i++)
if(light[i])
Bfs(i);
}
void DP()
{
f[0]=0;
for(int s=1;s<(1<<cntp);s++){ //这样枚举s,s从左往右的第一个是1的位置就是新增加的元素(即拿去匹配的元素)
g[s]=g[s>>1]+(s&1);
f[s]=INF;
if(g[s]%2) continue;
int newu=-1;
for(int i=0;i<cntp;i++){
if(s&(1<<i)){
if(newu==-1) newu=i;
else{
if(f[s-(1<<newu)-(1<<i)]!=INF)
f[s]=min(f[s],f[s-(1<<newu)-(1<<i)]+edge[newu+1][i+1]);
}
}
}
}
printf("%d\n",f[(1<<cntp)-1]==INF?-1:f[(1<<cntp)-1]);
}
int main()
{
read();
Getdist();
DP();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: