ACdream原创群赛(11)の风神日华神专场 H - XXX的机器人

来源:互联网 发布:淘宝上传图片怎么上传 编辑:程序博客网 时间:2024/04/28 15:03

H - XXX的机器人

Time Limit: 6000/3000MS (Java/Others) Memory Limit: 65536/32768KB (Java/Others)
SubmitStatus

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为一个数字。

置换群满足结合律,可以用两棵线段树维护分别维护正向与反向的指令。

 

方法一:可以用线段树计算出指令.

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>
 
usingnamespace std;
 
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
 
typedeflong long LL;
typedefunsigned longlong ULL;
 
constint maxn = 111111;
constint 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;
}
*/
//======================================================
 
inth[] = {0, 24, 6, 2, 1, 1};
 
structnode {
    inta[6];
    node() {
        for(inti = 1; i <= 5; i++) {
            a[i] = i;
        }
    }
    node(inttp[]) {
        for(inti = 1; i <= 5; i++) {
            a[i] = tp[i];
        }
    }
    node(intval) {
        intsta = 0;
        for(inti = 1; i <= 5; i++) {
            intk = val / h[i];
            val %= h[i];
            intcnt = 0;
            for(intj = 1; j <= 5; j++) {
                if( (sta & (1 << j)) == 0 ) {
                    if(cnt == k) {
                        sta |= (1 << j);
                        a[i] = j;
                        break;
                    }
                    else{
                        cnt++;
                    }
                }
            }
        }
    }
    node operator * (constnode &tp) const{
        node ret;
        for(inti = 1; i <= 5; i++) {
            ret.a[i] = tp.a[ a[i] ];
        }
        returnret;
    }
    node operator = (constnode &tp) {
        for(inti = 1; i <= 5; i++) {
            a[i] = tp.a[i];
        }
        return(*this);
    }
    voidshow() {
        for(inti = 1; i <= 5; i++) {
            if(i != 1) cout << ' ';
            cout << a[i];
        }
        cout << endl;
    }
    voidinput() {
        for(inti = 1; i <= 5; i++) {
            scanf("%d", a + i);
            //scanf(a[i]);
        }
    }
};
 
node room[maxn];
 
structSegTree {
    node num[maxn << 2];
    boolflag;
    voidbuild(intl, intr, intrt) {
        if(l == r) {
            num[rt] = room[l];
            return;
        }
        intm = (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(intll, intrr, intl, intr, intrt) {
        if(ll <= l && r <= rr) {
            returnnum[rt];
        }
        intm = (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);
        }
        returnret;
    }
}tree[2];
 
intget_hash(intnum[]) {
    intret = 0;
    for(inti = 1; i <= 5; i++) {
        intcnt = 0;
        for(intj = i + 1; j <= 5; j++) {
            if(num[i] > num[j]) {
                cnt++;
            }
        }
        ret += cnt * h[i];
    }
    returnret;
}
intdp[111][133];
node op[111];
node start;
intcnt[111];
 
intmain() {
//    freopen("robot.in", "r", stdin);
//    freopen("robot.out", "w", stdout);
 
    intn, m;
    while(~scanf("%d %d", &n, &m)) {
        for(inti = 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(inti = 1; i <= m; i++) {
            intl, 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(inti = 1; i <= m; i++) {
            for(intj=0;j<120;j++) dp[i][j]=dp[i-1][j];
            for(intj = 0; j < 120; j++) {
                if(dp[i-1][j] == inf) continue;
                node now(j);
                node next = now * op[i];
                intsta = 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]);
 
    }
    return0;
}

 

方法二: 也可以计算出前后缀

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>
usingnamespace std;
typedeflong long ll;
constint N=100005;
constint mod=1007;
constdouble eps=1e-8;
constint inf=(1<<30);
//-----------------------------------------------------------------------------
 
intfrom[3333]; //编号i对应的排列为from[i]
intto[3333];  //排列i对应的编号为to[i]
intHASH(intb[]) { //五进制压缩
    intx=0;
    for(inti=0;i<5;i++) x=x*5+(b[i]-1);
    returnx;
}
voidgetnum() { //对12345所有的排列进行编号映射
    intidx=0;
    intb[]={1,2,3,4,5};
    do{
        intx=0;
        for(inti=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));
}
 
inttran(intx,inty) { //x通过置换规则y变成ret
    inti,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;
 
    intret=0;
    for(i=0;i<5;i++) ret=ret*5+B[A[i]];
    returnret;
}
inttran2(intx,inty) { //x由置换规则ret变成y
    inti,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];
 
    intret=0;
    for(i=0;i<5;i++) ret=ret*5+C[i];
    returnret;
}
voidout(intx) {
    printf("```: ");
    inti,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("");
}
 
intn,m;
inta[N]; //每个房间的置换规则
inttl[N],tr[N]; //置换前缀,置换后缀
inttb[111],cnt[111]; //每个操作对应置换规则; 走过的房间数
intdp[111][122]; //dp[i][j] 前i个操作置换为排列j的最小房间数
 
intmain()
{
//    freopen("robot.in","r",stdin);
//    freopen("out2.txt","w",stdout);
    inti,j,t,cas=0;
    intS,T;
    intb[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]);
        intx=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;
                intc=tran(from[j],tb[i]);
                intz=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] ] ]);
    }
    return0;
}


0 0
原创粉丝点击