CF2051G Snakes

CF2051G Snakes

思路

很巧妙的状压 DP。

可以发现无论初始怎么摆放,结束后蛇之间的相对位置都不变,且最终的答案总是等于编号最大的蛇的蛇头位置。那么知道蛇的摆放顺序后,就要使得蛇之间的距离最小(此处将距离定义为一条蛇的蛇尾和上一条蛇的蛇头坐标之差)。

假如场上只有两条蛇,则只有两种操作可以改变蛇之间的距离:

  1. 后面的蛇伸长,此时距离 \(-1\)

  2. 前面的蛇缩短,此时距离 \(+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; 
}
posted @ 2025-03-26 11:04  WuMin4  阅读(17)  评论(0)    收藏  举报