CF2051G Snakes
CF2051G Snakes
思路
很巧妙的状压 DP。
可以发现无论初始怎么摆放,结束后蛇之间的相对位置都不变,且最终的答案总是等于编号最大的蛇的蛇头位置。那么知道蛇的摆放顺序后,就要使得蛇之间的距离最小(此处将距离定义为一条蛇的蛇尾和上一条蛇的蛇头坐标之差)。
假如场上只有两条蛇,则只有两种操作可以改变蛇之间的距离:
-
后面的蛇伸长,此时距离 \(-1\)。
-
前面的蛇缩短,此时距离 \(+1\)。
假设蛇的初始距离为 \(x\),则蛇之间的最小距离即为 \(x+d\),\(d\) 为操作时的距离与初始距离之差的最小值。
因为蛇之间的最小距离 \(x+d>0\),则初始距离最小值 \(x\) 即为 \(-d+1\)(此处的 \(d\) 一定不小于 \(0\),因为不操作时的距离之差为 \(0\))。
于是可以 \(O(n^2q)\) 求出任意两条蛇之间的最小距离再进行状压 DP。设 \(f_{s,i}\) 表示已经选择的蛇的状态为 \(s\),最后选择的蛇的编号为 \(i\) 时的距离之和。
状态转移方程即为:
\[f_{s+2^{i-1},i}=\min\{f_{s,lst}+dis_{lst,i}\}
\]
答案即为 \(min\{f_{2^n-1,i}+len_i\}\)(此处 \(len\) 为第 \(i\) 条蛇伸长的长度),时间复杂度 \(O(n^2q+2^nn^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
int x,y;
}a[200005];
int n,q,len[25],dis[25][25],f[1<<20][25],ans=INT_MAX;
signed main() {
cin>>n>>q;
for(int i=1;i<=q;i++){
char s;
cin>>a[i].x>>s;
if(s=='+') a[i].y=1,len[a[i].x]++;
else a[i].y=-1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
int res=0,mnr=0;
for(int k=1;k<=q;k++){
if(a[k].x==i&&a[k].y==1)
res--;
if(a[k].x==j&&a[k].y==-1)
res++;
mnr=min(mnr,res);
}
dis[i][j]=-mnr+1;
}
}
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)
f[1<<(i-1)][i]=1;
for(int s=0;s<(1<<n);s++){
for(int lst=1;lst<=n;lst++){
if(f[s][lst]!=0x3f3f3f3f){
for(int i=1;i<=n;i++){
if(!(s&(1<<(i-1)))){
f[s|(1<<(i-1))][i]=min(f[s|(1<<(i-1))][i],f[s][lst]+dis[lst][i]);
}
}
}
}
}
for(int i=1;i<=n;i++)
ans=min(ans,f[(1<<n)-1][i]+len[i]);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号