Description
有 n n 个开关,初始状态均为关闭状态,现在要通过若干操作打开其中的个开关 x1,...,xk x 1 , . . . , x k ,每次操作可以选取一个起点后反转该起点及之后共 a1,...,al a 1 , . . . , a l 个开关的状态,问最少要几步操作可以达到要求,如果无解则输出 −1 − 1
Input
第一行输入三个整数 n,k,l n , k , l 分别表示开关数,要打开的开关数以及操作种类数,之后输入 k k 个整数表示要打开的开关编号,最后输入 l l 个整数表示一次操作可以打开的连续开关数
(1≤n≤1000,1≤k≤10,1≤l≤100,1≤x1<x2<...<xk≤n) ( 1 ≤ n ≤ 1000 , 1 ≤ k ≤ 10 , 1 ≤ l ≤ 100 , 1 ≤ x 1 < x 2 < . . . < x k ≤ n )
Output
输出最少操作数,如果无解则输出 −1 − 1
Sample Input
10 8 2
1 2 3 5 6 7 8 9
3 5
Sample Output
2
Solution
定义状态: yi=1 y i = 1 表示第 i i 个开关状态与第个开关状态相同, yi=0 y i = 0 表示不同, 0≤i≤n 0 ≤ i ≤ n (不妨令 x0=xn+1=0 x 0 = x n + 1 = 0 )
那么初始状态 y0=...=yn=0 y 0 = . . . = y n = 0 ,目标状态是 yxi−1=yxi=1,1≤i≤k y x i − 1 = y x i = 1 , 1 ≤ i ≤ k ,假设一次操作反转了从 i i 开始的个开关,那么该步操作后, y y 中只有和 yi+a−1 y i + a − 1 状态改变,以此为关系建边,边权为 1 1 表示该步操作的代价,由于目标状态中为 1 1 的位置至多个,故可以从每点开始求一遍最短路进而得到将 yi=yj=0 y i = y j = 0 同时变成 1 1 的最小代价(最短路一方面保证操作数最少,另一方面保证了这一系列操作后只有这两个位置的值改变)
得到代价函数之后就可以状压了,把 y y 中期望变成的这 m m 个位置看作位 01 01 状压, dp[S] d p [ S ] 表示这 m m 个位置中的状态为 S S 时所需的最少操作步数,那么即为答案,每次转移只需选取状态 S S 中为的两个位置 i,j i , j ,进而有转移 dp[S]=min(dp[S],dp[S−2i−2j]+cost[i][j]) d p [ S ] = m i n ( d p [ S ] , d p [ S − 2 i − 2 j ] + c o s t [ i ] [ j ] )
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=10005;
int n,k,l,x[maxn],a[maxn],dis[maxn],vis[maxn],cost[22][22],id[maxn],dp[1<<22];
vector<int>g[maxn];
void spfa(int s)
{
memset(vis,0,sizeof(vis));
queue<int>que;
for(int i=0;i<maxn;i++)dis[i]=INF;
dis[s]=0,vis[s]=1;
que.push(s);
while(!que.empty())
{
int u=que.front();que.pop();
vis[u]=0;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(dis[v]>dis[u]+1)
{
dis[v]=dis[u]+1;
if(!vis[v])vis[v]=1,que.push(v);
}
}
}
}
void add(int u,int v)
{
g[u].push_back(v),g[v].push_back(u);
}
int main()
{
scanf("%d%d%d",&n,&k,&l);
memset(x,0,sizeof(x));
for(int i=1;i<=k;i++)
{
int temp;
scanf("%d",&temp);
x[temp]=1;
}
for(int i=0;i<=n;i++)
if(x[i]!=x[i+1])x[i]=1;
else x[i]=0;
for(int i=1;i<=l;i++)scanf("%d",&a[i]);
for(int i=0;i<=n;i++)
for(int j=1;j<=l;j++)
{
if(i-a[j]>=0&&x[i-a[j]]==0)add(i-a[j],i);
if(i+a[j]<=n)add(i,i+a[j]);
}
k=0;
for(int i=0;i<=n;i++)
if(x[i])id[i]=k++;
memset(cost,INF,sizeof(cost));
for(int i=0;i<=n;i++)
if(x[i])
{
spfa(i);
for(int j=0;j<=n;j++)
if(x[j])cost[id[i]][id[j]]=dis[j];
}
memset(dp,INF,sizeof(dp));
dp[0]=0;
int K=1<<k;
for(int i=1;i<K;i++)
{
int x,y;
for(x=0;x<k;x++)
if((i>>x)&1)break;
for(y=x+1;y<k;y++)
if((i>>y)&1)
dp[i]=min(dp[i],dp[i^(1<<x)^(1<<y)]+cost[x][y]);
}
if(dp[K-1]==INF)printf("-1\n");
else printf("%d\n",dp[K-1]);
return 0;
}