题解 P3943 【星空】
关于转换的部分,题解和下面的人其实讲的已经很清楚了,不过我想纠正一下下面的人一点,原题解说是bfs是没有问题的,因为每次更新的代价是1,你访问到一个点了,下次它再被更新肯定没有当前优,所以是O(n)的bfs。
还有就是希望不要有人像我一样,交了十几次,最后发现是K和M的输入搞反了。。。
我用的和原题解的方法一样,就是差分+状压+bfs(话说这是我第一次看到差分的拓展。。。)
然后我在程序里面注解了一下。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#define maxn 40009
#define maxx 1000000000
using namespace std;
int n,m,k,x,val[maxn],have[maxn],w[maxn],d[maxn],dis[20][20],dp[500009];
bool in[maxn];
queue<pair<int,int> >q;
int main()
{
scanf("%d%d%d",&n,&k,&m);
for (int i=0;i<=n+1;i++) val[i]=1;
for (int i=1;i<=k;i++)
{
scanf("%d",&x); val[x]=0;
}
k=0;
for (int i=1;i<=n+1;i++)
if (val[i]^val[i-1]) {have[i]=++k,w[k]=i;}//cout<<"!"<<i<<endl;}
for (int i=1;i<=m;i++) scanf("%d",&d[i]),d[i]++;
pair<int,int>now;int to;
for (int i=1;i<=k;i++)
for (int j=1;j<=k;j++)
if (i!=j) dis[i][j]=maxx;
//一个个入队列,做一遍bfs,时间复杂度为O(kn)
for (int i=1;i<=k;i++)
{
for (int j=0;j<=n+1;j++) in[j]=false;
q.push(make_pair(w[i],0)); in[w[i]]=true;
while (!q.empty())
{
now=q.front();q.pop();
for (int j=1;j<=m;j++)
{
to=now.first+d[j]-1;
if ((to>n+1)||(in[to])) continue;
q.push(make_pair(to,now.second+1));in[to]=true;
if (have[to]) dis[i][have[to]]=now.second+1;
}
for (int j=1;j<=m;j++)
{
to=now.first-d[j]+1;
if ((to<0)||(in[to])) continue;
q.push(make_pair(to,now.second+1));in[to]=true;
if (have[to]) dis[i][have[to]]=now.second+1;
}
}
}
for (int i=1;i<(1<<k);i++) dp[i]=maxx;
//就是选择当前状态下还没消除的两个点,将它们消掉的状态更新,不过和noip提高组2016Day2T3的愤怒的小鸟很像,反正当前状态下第一个
//物品肯定是要消的,那不如就直接选择第一个没被消的点来更新。
for (int i=0;i<(1<<k);i++)
{
int j=0;
while (i&(1<<j)) j++;
for (int p=j+1;p<k;p++)
if (!(i&(1<<p)))
dp[i|(1<<j)|(1<<p)]=min(dp[i|(1<<j)|(1<<p)],dp[i]+dis[j+1][p+1]);
}
printf("%d\n",dp[(1<<k)-1]);
return 0;
}