题目描述

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. Django(24)永久重定向和临时重定向

    重定向 重定向分为永久重定向和临时重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面.比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面. ...

  2. CSS中margin负值巧布局

    margin负值实现细边框 我们先准备五个div盒子,并设置好浮动和2px的实线黑色边框,看看效果 中间的边框线挨在了一起致使边框变粗成了4px,这时使用margin负值就可以解决这个问题 <s ...

  3. jQ的隐式迭代和设置样式属性

    jQ中的隐式迭代 意义:不需要原生迭代了,在jQ内部自动帮你实现了循环 代码实现: let arr = document.querySelectorAll('li') for(let i = 0;i ...

  4. [刷题] 17 Letter Combinations of a Phone Number

    要求 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合 1 不对应任何字母    示例 输入:"23" 输出:["ad", "ae&q ...

  5. 服务器硬件必须支持M2 或PCIE才能支持NVME

    兆芯服务器不支持NVME. 服务器硬件必须支持M2 或PCIE才能支持NVME.1 因为物理接口只有M2 SATA 和PCIE这三中但是NVME只支持M2 和PCIE这2种2所以 NVME不支持SAT ...

  6. Rust 多态

    Rust 多态 分发 多态的上下文中的方法解析过程被称为分发,调用该方法称为分发化,在支持多态的主流语言中,分发可以通过以下任意一种方式进行. 静态分发 当在编译期决定要调用的方法时,它被称为静态分发 ...

  7. STM32 中的CEC

    http://www.cnblogs.com/qdrs/articles/7645117.html

  8. EasyUI_使用datagrid分页 (Day_28)

    本次分页涉及技术点 SSM+PageHelper+DatagrId 先来看下效果: 这是无条件分页,下一篇博客我们将讲有条件分页. 无论你是使用js加载table 还是直接使用标签. 使用datagr ...

  9. USB中TOKEN的CRC5与CRC16校验(神奇的工具生成Verilog实现)

    USB2.0IP设计 最近,在学习USB2.0IP的设计,其中包含了CRC校验码的内容,之前学习千兆以太网曾经用到过CRC32校验(https://www.cnblogs.com/Xwangzi66/ ...

  10. lua type 获取 类型

    lua中的类型作一小记 print(type("Hello world")) --> string print(type(10.4*3)) --> number pri ...