题目描述

Lena喜欢秩序井然的生活。一天,她要去上大学了。突然,她发现整个房间乱糟糟的——她的手提包里的物品都散落在了地上。她想把所有的物品都放回她的手提包。但是,这里有一点问题:她一次最多只能拿两个物品,她也不能移动她的手提包。并且,因为她爱整洁的习惯,如果她拿起了一个物品,她也不能将它放在其他地方,除非放回她的手提包。

Lena把她的房间划分为了一个平面直角坐标系。现在Lena给你她的手提包和每个散落的物品的坐标(当然,一开始的时候她就和手提包站在一个地方)。她从坐标 $(x1,y1)$ 走到坐标 $(x2,y2)$ 需要用 $(x1-x2)^{2}+(y1-y2)^{2}$ 单位的时间。现在,Lena将告诉你她的房间的情况,请你为Lena找到一个拾起每个物品的顺序,使她拾起所有物品所需的总时间最小。当然,Lena最后需要返回她的手提包。

输入输出格式

输入格式

输入文件的第一行为Lena的手提包的坐标 $x_{s}​$ , $y_{s}$​ 。第二行为一个正整数 n ,表示总的需要拾起的物品数。接下来的 n 行每行包括两个整数,表示每个物品的坐标。

输出格式

输出的第一行为一个正整数,表示Lena拾起所有物品所需的最小时间。

输出的第二行为Lena拾起每个物品的顺序。(每一个物品由它的编号代表,0表示手提包)她应该从手提包(0)出发,在手提包(0)结束。

如,0 1 2 0 3 0

表示她从手提包出发,先拾起1号物品,再拾起2号物品,然后返回手提包(并放下1和2),再拾起3号物品,最后返回手提包。

如果有多条允许的路径,输出任一条。

输入输出样例

输入样例#1:

0 0
2
1 1
-1 1

输出样例#1:

0 0
8
0 1 2 0

输入样例#2:

0 0
1 1
3
4 3
3 4
0 0

输出样例#2:

0 0
32
0 1 2 0 3 0

思路

状压DP

以当前已经取了哪些物品作为状态。

$n<=24$ 的数据范围和512MB的空间限制基本上就标志着这道题是一个标准的状压。

为了节省空间,我们就用 $1<<i-1$ 表示第 i 个物品有无被取过。由于一次可以选择拿一个或者两个物品,考虑在状态转移的时候枚举这次拿的两个物品(如果两个物品相同就处理为这次只拿一个)。

由于要输出拿的方法,我们就再用一个数组记录当前状态是从之前的哪个状态转移过来的即可。

需要注意的是,可以证明,若设每次离开手提包捡拾物品为一次,则各次之间的顺序不影响最终答案。

如,第一次拿物品1,第二次拿物品2,3与第一次拿物品2,3,第二次拿物品1的所用时间是一样的。这样,我们在转移的时候就从编号小的物品向编号大的物品找寻,只要找到了当前的一种可用方案就可以进入下一个状态了,这样可以节省时间(同时避免重复计算)。

代码

#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register int
using namespace std;
const int MAXN=24, INF=2e9;
struct obj
{
int x, y;
} things[MAXN+1];
int n;
int dp[1<<MAXN|1], pre[1<<MAXN|1];
int dis[MAXN+1][MAXN+1];
inline int min( int a, int b ) { return a<b?a:b; }
inline int read(){
int x=0,w=1;
char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x*w;
}
inline void solve();
int main( )
{
for(re i=0;i<=(1<<MAXN);++i) dp[i]=INF,pre[i]=0;
things[0].x=read();
things[0].y=read();
n=read();
for(re i=1;i<=n;++i) things[i].x=read(),things[i].y=read();
for(re i=0;i<=n;++i) for(re j=0;j<=n;++j)
{
dis[i][j]=dis[j][i]=
(things[i].x-things[j].x)*(things[i].x-things[j].x)+
(things[i].y-things[j].y)*(things[i].y-things[j].y);
}
solve();
printf("%d\n",dp[(1<<n)-1]);
int now=(1<<n)-1; //从全部拿完的状态开始往前推
while (now!=0)
{
printf("0 ");
int update=now^pre[now];
for(int i=1;i<=n;i++) if(update&1<<i-1) printf("%d ",i);
now=pre[now];
}
printf("0\n");
return 0;
} inline void solve( )
{
dp[0]=0,pre[0]=0;
for(int m=0;m<(1<<n);m++)
{
if(dp[m]==INF) continue; //如果当前状态没有被更新过直接continue
for(int i=1;i<=n;i++)
{
if(m&1<<i-1) continue; //如果已经拿过了
for(int j=1;j<=n;j++)
{
if(m&1<<j-1) continue;
if(dp[m|(1<<i-1)|(1<<j-1)]>dp[m]+dis[0][i]+dis[i][j]+dis[j][0])
{
dp[m|(1<<i-1)|(1<<j-1)]=dp[m]+dis[0][i]+dis[i][j]+dis[j][0];
pre[m|(1<<i-1)|(1<<j-1)]=m; //状态转移
}
}
break;
}
}
return;
}

转自星烁晶熠辉

【题解】codeforces 8c Looking for Order 状压dp的更多相关文章

  1. codeforces 8C. Looking for Order 状压dp

    题目链接 给n个物品的坐标, 和一个包裹的位置, 包裹不能移动. 每次最多可以拿两个物品, 然后将它们放到包里, 求将所有物品放到包里所需走的最小路程. 直接状压dp就好了. #include < ...

  2. Codeforces Beta Round #8 C. Looking for Order 状压dp

    题目链接: http://codeforces.com/problemset/problem/8/C C. Looking for Order time limit per test:4 second ...

  3. Codeforces Round #363 LRU(概率 状压DP)

    状压DP: 先不考虑数量k, dp[i]表示状态为i的概率,状态转移方程为dp[i | (1 << j)] += dp[i],最后考虑k, 状态表示中1的数量为k的表示可行解. #incl ...

  4. Codeforces 429C Guess the Tree(状压DP+贪心)

    吐槽:这道题真心坑...做了一整天,我太蒻了... 题意 构造一棵 $ n $ 个节点的树,要求满足以下条件: 每个非叶子节点至少包含2个儿子: 以节点 $ i $ 为根的子树中必须包含 $ c_i ...

  5. Codeforces 895C Square Subsets(状压DP 或 异或线性基)

    题目链接  Square Subsets 这是白书原题啊 先考虑状压DP的做法 $2$到$70$总共$19$个质数,所以考虑状态压缩. 因为数据范围是$70$,那么我们统计出$2$到$70$的每个数的 ...

  6. Codeforces 895C Square Subsets:状压dp【组合数结论】

    题目链接:http://codeforces.com/problemset/problem/895/C 题意: 给你n个数a[i].(n <= 10^5, 1 <= a[i] <= ...

  7. CodeForces 599E Sandy and Nuts 状压DP

    题意: 有一棵\(n(1 \leq n \leq 13)\)个节点的树,节点的标号为\(1 \sim n\),它的根节点是\(1\). 现在已知它的\(m(0 \leq m < n)\)条边,和 ...

  8. codeforces 580D Kefa and Dishes(状压dp)

    题意:给定n个菜,每个菜都有一个价值,给定k个规则,每个规则描述吃菜的顺序:i j w,按照先吃i接着吃j,可以多增加w的价值.问如果吃m个菜,最大价值是多大.其中n<=18 思路:一看n这么小 ...

  9. Codeforces 342D Xenia and Dominoes 状压dp

    码就完事了. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define ...

随机推荐

  1. 改善c++程序的150个建议(读后总结)-------12-18

    12.优先使用前置操作符 #include <iostream> using namespace std; class A { private: int num; public: A op ...

  2. MySQL修改账号密码方法大全

    前言: 在日常使用数据库的过程中,难免会遇到需要修改账号密码的情景,比如密码太简单需要修改.密码过期需要修改.忘记密码需要修改等.本篇文章将会介绍需要修改密码的场景及修改密码的几种方式. 1.忘记 r ...

  3. JVM虚拟机-垃圾回收机制与垃圾收集器概述

    目录 前言 什么是垃圾回收 垃圾回收的区域 垃圾回收机制 流程 怎么判断对象已经死亡 引用计数法 可达性分析算法 不可达的对象并非一定会回收 关于引用 强引用(StrongReference) 软引用 ...

  4. PSP初体验:求交点

    项目 内容 课程:北航2020春软件工程 博客园班级博客 作业:完成一个平面图形求交点的程序,体验PSP的过程 个人项目作业 我在这个课程的目标是 体验软件开发的全流程 这个作业在哪个具体方面帮助我实 ...

  5. 29.Map,可变参数

    1.Map集合 1.1Map集合概述和特点[理解] 单列集合一次存一个元素 双列集合一次存两个元素 键:不能重复的        值:可以重复的 Map集合概述 interface Map<K, ...

  6. 重新整理 .net core 实践篇————依赖注入应用[二]

    前言 这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块. 以下只是个人整理,如有问题, ...

  7. GCC链接时库顺序问题

    GCC或G++在编译链接时,如果命令行中含有库,则要特别注意了.根据<C专家编程>5.3节中的提示,GCC在链接时对命令行时的处理顺序是从左到右.证据是GCC的MAN: -l librar ...

  8. CentOS7启动SSH服务报:Job for ssh.service failed because the control process exited with error code

    CentOS7启动SSH服务报:Job for ssh.service failed because the control process exited with error code....... ...

  9. 041.Python守护进程,锁信号量和事件

    一 守护进程 1.1 基本概念 守护进程 正常情况下,主进程默认等待子进程调用结束之后结束 守护进程在主进程执行代码结束后,自动终止 守护进程语法: 进程对象.daemon = True ,设置该进程 ...

  10. ltp 测试流程及测试脚本分析

    LTP介绍 (2011-03-25 18:03:53) 转载▼ 标签: ltp linux 压力测试 杂谈 分类: linux测试 LTP介绍 一.LTP介绍1.简介LTP(Linux Test Pr ...