Description
有 n n 个开关,初始状态均为关闭状态,现在要通过若干操作打开其中的 k k 个开关 x1,...,xk x 1 , . . . , x k ,每次操作可以选取一个起点后反转该起点及之后共 a1,...,al a 1 , . . . , a l 个开关的状态,问最少要几步操作可以达到要求,如果无解则输出 −1 − 1
Input
第一行输入三个整数 n,k,l n , k , l 分别表示开关数,要打开的开关数以及操作种类数,之后输入 k k 个整数 x1,...,xk x 1 , . . . , x k 表示要打开的开关编号,最后输入 l l 个整数 a1,...,al a 1 , . . . , a 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 个开关状态与第 i+1 i + 1 个开关状态相同, 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 开始的 a a 个开关,那么该步操作后, y y 中只有 yi−1 y i − 1 和 yi+a−1 y i + a − 1 状态改变,以此为关系建边,边权为 1 1 表示该步操作的代价,由于目标状态中 y y 为 1 1 的位置至多 2k 2 k 个,故可以从每点开始求一遍最短路进而得到将 yi=yj=0 y i = y j = 0 同时变成 1 1 的最小代价 cost[i][j] c o s t [ i ] [ j ] (最短路一方面保证操作数最少,另一方面保证了这一系列操作后只有这两个位置的值改变)
得到代价函数之后就可以状压了,把 y y 中期望变成 1 1 的这 m m 个位置看作 m m 位 01 01 状压, dp[S] d p [ S ] 表示这 m m 个位置中 1 1 的状态为 S S 时所需的最少操作步数,那么 dp[2m−1] d p [ 2 m − 1 ] 即为答案,每次转移只需选取状态 S S 中为 1 1 的两个位置 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;i0,vis[s]=1;
que.push(s);
while(!que.empty())
{
int u=que.front();que.pop();
vis[u]=0;
for(int i=0;iint 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<for(int i=1;iint x,y;
for(x=0;xif((i>>x)&1)break;
for(y=x+1;yif((i>>y)&1)
dp[i]=min(dp[i],dp[i^(1<1<if(dp[K-1]==INF)printf("-1\n");
else printf("%d\n",dp[K-1]);
return 0;
}