CodeForces 8C Looking for Order
题目
题目大意
在一个平面直角坐标系中散落着 N N 件物品,每件物品的坐标为整数,标号为。定义两点 A,B A , B 间距离为 (Ax−Bx)2+(Ay−By)2 ( A x − B x ) 2 + ( A y − B y ) 2 。给定收纳箱的位置,将收纳箱标号为0。在收纳箱处有一个人,她想将所有物品收集到收纳箱处,但她每次只能带最多两件物品,求她最少需要行走的距离。
思路
由于 N N 很小,所以我们可以考虑状压DP。
我们定义:在二进制位中,0表示已经拿了,1表示还没被拿;记为 N N 位二进制数。定义为对于状态 S S ,至少要走的最短距离。
我们记为编号为 i,j i , j 的两个物品的距离。
由于在题目中没有对取的顺序进行限制,我们就默认先取编号小的物品。
如果我们只取一件物品,则最短距离为 f[S−2i]+2×d(i,0) f [ S − 2 i ] + 2 × d ( i , 0 ) 。如果我们取两件物品,则最短距离为 f[S−2i−2j]+d(0,i)+d(i,j)+d(j,0)(i≠j) f [ S − 2 i − 2 j ] + d ( 0 , i ) + d ( i , j ) + d ( j , 0 ) ( i ≠ j )
我们可得状态转移方程:
输出路径时,需要记录一个pre
数组,该路径即为
S
S
和中相差的1的位置。
正解代码
注意:在此代码中,我将物品编号改为 0…N−1 0 … N − 1 ,收纳箱编号为 N N <script type="math/tex" id="MathJax-Element-1893">N</script>。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=24;
const int Maxs=(1<<Maxn);
const int INF=0x3f3f3f3f;
int N,sx,sy;
int X[Maxn+5],Y[Maxn+5];
int dis[Maxn+5][Maxn+5];
int f[Maxs+5],pre[Maxs+5];
void Init() {
X[N]=sx,Y[N]=sy;
for(int i=0;i<=N;i++)
for(int j=0;j<=N;j++)
dis[i][j]=(X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j]);
}
int DFS(int S) {
if(f[S]!=-1)return f[S];
f[S]=INF;
for(int i=0;i<N;i++)
if(S&(1<<i)) {
int T1=S^(1<<i);
int tmp=DFS(T1);
if(f[S]>tmp+2*dis[i][N]) {
f[S]=tmp+2*dis[i][N];
pre[S]=T1;
}
for(int j=i+1;j<N;j++)
if(S&(1<<j)) {
int T2=T1^(1<<j);
tmp=DFS(T2);
if(f[S]>tmp+dis[N][i]+dis[i][j]+dis[j][N]) {
f[S]=tmp+dis[N][i]+dis[i][j]+dis[j][N];
pre[S]=T2;
}
}
break;
}
return f[S];
}
void Print(int S) {
if(S==0)return;
for(int i=0;i<N;i++)
if((S^pre[S])&(1<<i)) {
printf("%d ",i+1);
}
printf("0 ");
Print(pre[S]);
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d %d",&sx,&sy,&N);
for(int i=0;i<N;i++)
scanf("%d %d",&X[i],&Y[i]);
Init();
memset(f,-1,sizeof f);
f[0]=0;
printf("%d\n",DFS((1<<N)-1));
printf("0 ");Print((1<<N)-1);
return 0;
}