ACdream原创群赛(11)の风神日华神专场 H - XXX的机器人
来源:互联网 发布:淘宝上传图片怎么上传 编辑:程序博客网 时间:2024/04/28 15:03
H - XXX的机器人
Problem Description
XX手里有5张卡片,卡片上的数字分别是1~5的某个全排列a[l], a[2], a[3], a[4], a[5](比如2 3 1 5 4)。
有n个房间。每个房间都有一个卡片转化规则,每个规则也是1~5的某个全排列b[l], b[2], b[3], b[4], b[5](比如 3 4 2 1 5)。
每进入到一个房间手里的卡片会根据当前房间的规则做相应的变换:a[i] = b[ a[i] ] (1 <= i <= 5)。
相邻的两个房间是相连的,也就是说第i个房间可以走到i+1房间和i-1房间(如果i+1房间和i-1房间存在的话)。
XX的目标是使手里的卡片变成1 2 3 4 5。
由于XX要和奶茶妹妹约会,没有时间,所有他派遣一个机器人去帮他。并且写了m条指令。
每一条指令的格式是S T, 表示从S房间走到T房间。
这m条指令只能按顺序执行。对于每条指令机器人可以选择执行或不执行。
这个机器人想知道最少要路过多少个房间可以达到XX的要求。
Input
输入有多组数据,对于每组数据:
第一行为两个数字n, m(2<=n<=100000, 0<= m <= 100),表示有n个房间,有m条指令。
接下来有n行,每行5个数字(为1~5的某个全排列),表示每一个房间的转换规则。
然后有m行,每一行有两个数字S, T( 1<=S, T<=n, S != T)表示每条指令。
最后一行有5个数字(为1~5的某个全排列),表示XX初始时手里的卡片。
Output
对于每组数据,输出最小值。如果没有满足条件的最小值,那么输出-1。
Sample Input
2 21 5 2 3 41 3 5 2 42 12 11 2 4 5 3
Sample Output
4
Hint
只有这两条指令都执行才能达到要求,所以要路过4个房间
H题
Dp[i][j]表示执行了i指令之后状态为j的 路过最小房间数。 j可以用康托展开把全排列hash为一个数字。
置换群满足结合律,可以用两棵线段树维护分别维护正向与反向的指令。
方法一:可以用线段树计算出指令.
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using
namespace
std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
typedef
long
long
LL;
typedef
unsigned
long
long
ULL;
const
int
maxn = 111111;
const
int
inf = 0x7f7f7f7f;
//=====================================================
/*
inline int scanf(int &num)
{
char in;
bool neg=false;
while((in=getchar())!=EOF && (in>'9' || in<'0') && in!='-');
if(in==EOF) return 0;
else if(in=='-') neg=true,num=0;
else num=in-'0';
while(in=getchar(),in>='0' && in<='9') num*=10,num+=in-'0';
if(neg) num=-num;
return 1;
}
*/
//======================================================
int
h[] = {0, 24, 6, 2, 1, 1};
struct
node {
int
a[6];
node() {
for
(
int
i = 1; i <= 5; i++) {
a[i] = i;
}
}
node(
int
tp[]) {
for
(
int
i = 1; i <= 5; i++) {
a[i] = tp[i];
}
}
node(
int
val) {
int
sta = 0;
for
(
int
i = 1; i <= 5; i++) {
int
k = val / h[i];
val %= h[i];
int
cnt = 0;
for
(
int
j = 1; j <= 5; j++) {
if
( (sta & (1 << j)) == 0 ) {
if
(cnt == k) {
sta |= (1 << j);
a[i] = j;
break
;
}
else
{
cnt++;
}
}
}
}
}
node operator * (
const
node &tp)
const
{
node ret;
for
(
int
i = 1; i <= 5; i++) {
ret.a[i] = tp.a[ a[i] ];
}
return
ret;
}
node operator = (
const
node &tp) {
for
(
int
i = 1; i <= 5; i++) {
a[i] = tp.a[i];
}
return
(*
this
);
}
void
show() {
for
(
int
i = 1; i <= 5; i++) {
if
(i != 1) cout <<
' '
;
cout << a[i];
}
cout << endl;
}
void
input() {
for
(
int
i = 1; i <= 5; i++) {
scanf
(
"%d"
, a + i);
//scanf(a[i]);
}
}
};
node room[maxn];
struct
SegTree {
node num[maxn << 2];
bool
flag;
void
build(
int
l,
int
r,
int
rt) {
if
(l == r) {
num[rt] = room[l];
return
;
}
int
m = (l + r) >> 1;
if
(flag) {
build(lson);
build(rson);
num[rt] = num[rt << 1] * num[rt << 1 | 1];
}
else
{
build(rson);
build(lson);
num[rt] = num[rt << 1 | 1] * num[rt << 1];
}
}
node query(
int
ll,
int
rr,
int
l,
int
r,
int
rt) {
if
(ll <= l && r <= rr) {
return
num[rt];
}
int
m = (l + r) >> 1;
node ret;
if
(flag) {
if
(ll <= m) ret = ret * query(ll, rr, lson);
if
(rr > m) ret = ret * query(ll, rr, rson);
}
else
{
if
(rr > m) ret = ret * query(ll, rr, rson);
if
(ll <= m) ret = ret * query(ll, rr, lson);
}
return
ret;
}
}tree[2];
int
get_hash(
int
num[]) {
int
ret = 0;
for
(
int
i = 1; i <= 5; i++) {
int
cnt = 0;
for
(
int
j = i + 1; j <= 5; j++) {
if
(num[i] > num[j]) {
cnt++;
}
}
ret += cnt * h[i];
}
return
ret;
}
int
dp[111][133];
node op[111];
node start;
int
cnt[111];
int
main() {
// freopen("robot.in", "r", stdin);
// freopen("robot.out", "w", stdout);
int
n, m;
while
(~
scanf
(
"%d %d"
, &n, &m)) {
for
(
int
i = 1; i <= n; i++) {
room[i].input();
}
tree[0].flag = 0;
tree[1].flag = 1;
tree[0].build(1, n, 1);
tree[1].build(1, n, 1);
for
(
int
i = 1; i <= m; i++) {
int
l, r;
scanf
(
"%d%d"
, &l, &r);
//scanf(l); scanf(r);
op[i] = tree[l < r].query(min(l, r), max(l, r), 1, n, 1);
cnt[i] = max(l, r) - min(l, r) + 1;
}
start.input();
memset
(dp, 0x7f,
sizeof
(dp));
dp[0][ get_hash(start.a) ] = 0;
for
(
int
i = 1; i <= m; i++) {
for
(
int
j=0;j<120;j++) dp[i][j]=dp[i-1][j];
for
(
int
j = 0; j < 120; j++) {
if
(dp[i-1][j] == inf)
continue
;
node now(j);
node next = now * op[i];
int
sta = get_hash(next.a);
dp[i][sta] = min(dp[i][sta], dp[i-1][j] + cnt[i]);
}
}
if
(dp[m][0] == inf) dp[m][0] = -1;
printf
(
"%d\n"
, dp[m][0]);
}
return
0;
}
方法二: 也可以计算出前后缀
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
using
namespace
std;
typedef
long
long
ll;
const
int
N=100005;
const
int
mod=1007;
const
double
eps=1e-8;
const
int
inf=(1<<30);
//-----------------------------------------------------------------------------
int
from[3333];
//编号i对应的排列为from[i]
int
to[3333];
//排列i对应的编号为to[i]
int
HASH(
int
b[]) {
//五进制压缩
int
x=0;
for
(
int
i=0;i<5;i++) x=x*5+(b[i]-1);
return
x;
}
void
getnum() {
//对12345所有的排列进行编号映射
int
idx=0;
int
b[]={1,2,3,4,5};
do
{
int
x=0;
for
(
int
i=0;i<5;i++) x=x*5+(b[i]-1);
from[++idx]=x;
//编号idx对应排列为x
to[x]=idx;
//排列x对应编号为idx
}
while
(next_permutation(b,b+5));
}
int
tran(
int
x,
int
y) {
//x通过置换规则y变成ret
int
i,A[6],B[6];
for
(i=4;i>=0;x/=5,i--) A[i]=x%5;
for
(i=4;i>=0;y/=5,i--) B[i]=y%5;
int
ret=0;
for
(i=0;i<5;i++) ret=ret*5+B[A[i]];
return
ret;
}
int
tran2(
int
x,
int
y) {
//x由置换规则ret变成y
int
i,A[6],B[6],C[6];
for
(i=4;i>=0;x/=5,i--) A[i]=x%5;
for
(i=4;i>=0;y/=5,i--) B[i]=y%5;
for
(i=0;i<5;i++) C[A[i]]=B[i];
int
ret=0;
for
(i=0;i<5;i++) ret=ret*5+C[i];
return
ret;
}
void
out(
int
x) {
printf
(
"```: "
);
int
i,A[6];
for
(i=4;i>=0;x/=5,i--) A[i]=x%5;
for
(i=0;i<5;i++)
printf
(
"%d "
,A[i]+1);
puts
(
""
);
}
int
n,m;
int
a[N];
//每个房间的置换规则
int
tl[N],tr[N];
//置换前缀,置换后缀
int
tb[111],cnt[111];
//每个操作对应置换规则; 走过的房间数
int
dp[111][122];
//dp[i][j] 前i个操作置换为排列j的最小房间数
int
main()
{
// freopen("robot.in","r",stdin);
// freopen("out2.txt","w",stdout);
int
i,j,t,cas=0;
int
S,T;
int
b[6];
getnum();
while
(
scanf
(
"%d%d"
,&n,&m)!=EOF) {
memset
(dp,-1,
sizeof
(dp));
for
(i=0;i<5;i++) b[i]=i+1;
tl[0]=tr[n+1]=a[0]=HASH(b);
for
(i=1;i<=n;i++) {
for
(j=0;j<5;j++)
scanf
(
"%d"
,&b[j]);
a[i]=HASH(b);
}
for
(i=1;i<=n;i++) tl[i]=tran(tl[i-1],a[i]);
for
(i=n;i>=1;i--) tr[i]=tran(tr[i+1],a[i]);
for
(i=1;i<=m;i++) {
scanf
(
"%d%d"
,&S,&T);
if
(S<T) {
tb[i]=tran2(tl[S-1],tl[T]);
cnt[i]=T-S+1;
}
else
{
tb[i]=tran2(tr[S+1],tr[T]);
cnt[i]=S-T+1;
}
}
for
(i=0;i<5;i++)
scanf
(
"%d"
,&b[i]);
int
x=HASH(b);
dp[0][to[x]]=0;
for
(i=1;i<=m;i++) {
for
(j=1;j<=120;j++) dp[i][j]=dp[i-1][j];
for
(j=1;j<=120;j++) {
if
(dp[i-1][j]==-1)
continue
;
int
c=tran(from[j],tb[i]);
int
z=to[c];
if
(dp[i][z]==-1||dp[i][z]>dp[i-1][j]+cnt[i])
dp[i][z]=dp[i-1][j]+cnt[i];
}
}
printf
(
"%d\n"
,dp[m][ to[ a[0] ] ]);
}
return
0;
}
- ACdream原创群赛(11)の风神日华神专场 H - XXX的机器人
- ACdream原创群赛(11)の风神日华神专场 C.神奇的%系列一
- ACdream原创群赛(11)の风神日华神专场 G - 风之国
- ACdream原创群赛(13)のwuyiqi退役专场:LSS
- ACdream原创群赛(13)のwuyiqi退役专场
- ACdream原创群赛(13)のwuyiqi退役专场
- ACdream原创群赛(13)のwuyiqi退役专场 F:The Arrow (概率dp)
- ACdream原创群赛(13)のwuyiqi退役专场 C True love
- Acdream的暴力专场
- 【ACdream】ACdream原创群赛(18)のAK's dream
- 拓扑排序 I - barty的智商 acdream ACdream原创群赛(12)のBUAA选拔赛
- Acdream 1076 XXX的机器人(dp + 线段树)
- 数论 D - 寒假安排 --acdream ACdream原创群赛(12)のBUAA选拔赛
- 数论 A - 梦 -----acdream ACdream原创群赛(12)のBUAA选拔赛
- [ACdream原创群赛の数树]解题报告
- ACdream原创群赛(12)のBUAA选拔赛
- [ACdream原创群赛(12)のBUAA选拔赛]E:签到
- ACdream原创群赛(14)の我今天没吃药
- 中华编程知识传播联盟CPDL行动纲领草案
- 【ThinkingInC++】23、一个袖珍的C库
- 《STL源码剖析》---stl_multiset.h阅读笔记
- 【ThinkingInC++】24、基本对象,用struct写一个类
- 签个到
- ACdream原创群赛(11)の风神日华神专场 H - XXX的机器人
- linux上安装mysql5.6.4
- ERROR L104: MULTIPLE PUBLIC DEFINITIONS
- Red5-1.0-RC1整合到tomcat,logback报错问题
- [历史] 明太祖朱元璋直系后裔 - 朱镕基
- HDU 4849 Wow! Such City! 最短路
- 交换排序_2.快速排序
- 【畅言】程序员可以只关心技术么?
- STL vector中的emplace方法(23)