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 2 1 5 2 3 4 1 3 5 2 4 2 1 2 1 1 2 4 5 3
Sample Output
4
Hint
只有这两条指令都执行才能达到要求,所以要路过4个房间
H题
Dp[i][j]表示执行了i指令之后状态为j的 路过最小房间数。 j可以用康托展开把全排列hash为一个数字。
置换群满足结合律,可以用两棵线段树维护分别维护正向与反向的指令。
方法一:可以用线段树计算出指令.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
#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;
}
|
方法二: 也可以计算出前后缀
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#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;
}
|