1. 题目描述
给定一个$2 \times 2 \times 2$的魔方,当某个面上的4个小块颜色均相同时,称这个面为complete。求对这个魔方进行$n \in [1,7]$次旋转(沿某个面顺时针或者逆时针)的过程中,求complete的面总和的最大值。魔方及索引如下图所示:

2. 基本思路及代码
解法一(MLE):
刚读过题,觉得挺简单的,时限10000ms还挺长的。需要搞清楚沿着某个面旋转后矩阵的元素是如何变化的。
就想到可以进行状态压缩$6^24 < 2^63$,因此将cube表示的矩阵可以使用long long压缩状态,然后再带上已经旋转的次数作为key,记忆化搜索就好了。代码如下:

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
//#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 typedef struct {
int st, t;
} node_t; map<pair<LL,int>, int> tb;
map<pair<LL,int>, int>::iterator iter;
LL mask[];
int a[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int nxt[];
int unxt[]; void init() {
mask[] = ;
rep(i, , )
mask[i] = mask[i-] << ; rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
}
} LL zip(int *a) {
LL ret = ; per(i, , )
ret = ret* + a[i]; return ret;
} void unzip(LL val, int *a) {
rep(i, , ) {
a[i] = val % ;
val /= ;
}
} int calc(LL st) {
static int b[];
int ret = ;
unzip(st, b); rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} LL move_clock(LL st, int id) {
static int b[];
static int c[];
int i, j, *mf = movf[id]; unzip(st, b);
memcpy(c, b, sizeof(c)); for (i=; i<; ++i)
c[mf[nxt[i]]] = b[mf[i]];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = b[mf[(i<<)+]];
c[mf[(nxt[i]<<|)+]] = b[mf[(i<<|)+]];
} return zip(c);
} LL move_unclock(LL st, int id) {
static int b[];
static int c[];
int i, j, *mf = movf[id]; unzip(st, b);
memcpy(c, b, sizeof(c)); for (i=; i<; ++i)
c[mf[unxt[i]]] = b[mf[i]];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = b[mf[(i<<)+]];
c[mf[(unxt[i]<<|)+]] = b[mf[(i<<|)+]];
} return zip(c);
} int dfs(LL st, int n) {
pair<LL,int> p = mp(st, n); iter = tb.find(p);
if (iter != tb.end())
return iter->sec; int ret = ; ret = calc(st); LL nst; #ifndef ONLINE_JUDGE
int b[];
#endif if (n) {
rep(i, , ) {
nst = move_clock(st, i);
#ifndef ONLINE_JUDGE
unzip(nst, b);
rep(j, , )
printf("%d ", b[j]);
putchar('\n');
#endif
ret = max(ret, dfs(nst, n-));
nst = move_unclock(st, i);
#ifndef ONLINE_JUDGE
unzip(nst, b);
rep(j, , )
printf("%d ", b[j]);
putchar('\n');
#endif
ret = max(ret, dfs(nst, n-));
}
} tb[p] = ret; return ret;
} void solve() {
LL st = zip(a);
int ans = ; // tb.clr(); ans = dfs(st, n); printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

交了一下,果断MLE了。分析为什么?显然状态太多了,因为同一个魔方因为摆放位置不同,表示的数组也不同,导致状态太多,记忆化搜索时存的东西太多。

解法二(TLE):
因为$n$很小,所以果断去掉了记忆化的map,抱着$n$很小但时限很长的侥幸心理,试试。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
//#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 int a[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int nxt[];
int unxt[]; void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
}
} LL zip(int *a) {
LL ret = ; per(i, , )
ret = ret* + a[i]; return ret;
} void unzip(LL val, int *a) {
rep(i, , ) {
a[i] = val % ;
val /= ;
}
} int calc(LL st) {
static int b[];
int ret = ;
unzip(st, b); rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} LL move_clock(LL st, int id) {
static int b[];
static int c[];
int i, j, *mf = movf[id]; unzip(st, b);
memcpy(c, b, sizeof(c)); for (i=; i<; ++i)
c[mf[nxt[i]]] = b[mf[i]];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = b[mf[(i<<)+]];
c[mf[(nxt[i]<<|)+]] = b[mf[(i<<|)+]];
} return zip(c);
} LL move_unclock(LL st, int id) {
static int b[];
static int c[];
int i, j, *mf = movf[id]; unzip(st, b);
memcpy(c, b, sizeof(c)); for (i=; i<; ++i)
c[mf[unxt[i]]] = b[mf[i]];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = b[mf[(i<<)+]];
c[mf[(unxt[i]<<|)+]] = b[mf[(i<<|)+]];
} return zip(c);
} int dfs(LL st, int n) {
int ret = ; ret = calc(st); LL nst; if (n) {
rep(i, , ) {
nst = move_clock(st, i);
ret = max(ret, dfs(nst, n-));
nst = move_unclock(st, i);
ret = max(ret, dfs(nst, n-));
}
} return ret;
} void solve() {
LL st = zip(a);
int ans = ; ans = dfs(st, n); printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

果断TLE了。分析为什么?最坏情况下深搜的深度为7,每次深搜有12种旋转方式($6 \text{ faces } \times 2 \text{ directions }$)。
这棵搜索树的毛病太多了:结点太多($12^7 = 35831808$),存储消耗的也厉害。怎么办?

解法三(AC 717ms):
显然需要尽可能的减少旋转方式,观察6个面,两两相对。不妨令$F_a$与$F_b$相对,观察和想象可以发现对$F_a$进行顺时针旋转和对$F_b$进行逆时针旋转得到是相同配置的魔方。这样我们可以将旋转方式减少到6种,$6^7 = 279936$已经足够小了。这次果然AC,时间717ms,说明数据还是比较小的。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 int a[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
// {18,19,17,16, 20,21,15,14,13,12,11,10,},
{,,,, ,,,,,,,},
// {9,8,14,15, 1,3,7,13,17,19,21,23},
{,,,, ,,,,,,,}
// {22,23,21,20, 0,1,9,15,19,18,10,4},
};
int movp[][];
int nxt[];
int unxt[];
int ans; void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
}
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} inline void move(int *b, int *c, int id) {
int i; for (i=; i<; ++i) c[i] = b[movp[id][i]];
} void dfs(int *a, int n) {
int b[]; ans = max(ans, calc(a)); if (n) {
rep(i, , ) {
move(a, b, i);
dfs(b, n-);
}
}
} void solve() {
ans = ; dfs(a, n); printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法四(AC 265ms):
能不能再优化?为了优化这个深搜,显然要剪枝。基本思路是启发式函数或者剪掉重复状态。没想到太好的启发函数,因此思考能不能对已经出现的配置的魔方不再进行深搜(类似于方法一的记忆化搜索)。考虑对同一个面先顺时针旋转后立即进行逆时针旋转是没有意义的,因此这个可以剪枝。将717ms优化到265ms,效果还不错。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 int a[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
// {18,19,17,16, 20,21,15,14,13,12,11,10,},
{,,,, ,,,,,,,},
// {9,8,14,15, 1,3,7,13,17,19,21,23},
{,,,, ,,,,,,,}
// {22,23,21,20, 0,1,9,15,19,18,10,4},
};
int movp[][];
int nxt[];
int unxt[];
int ans; void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
}
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} inline void move(int *b, int *c, int id) {
int i; for (i=; i<; ++i) c[i] = b[movp[id][i]];
} void dfs(int *a, int n, int pre) {
int b[]; ans = max(ans, calc(a)); if (n) {
rep(i, , ) {
if ((i^) == pre)
continue;
move(a, b, i);
dfs(b, n-, i);
}
}
} void solve() {
ans = ; dfs(a, n, -); printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法五(AC 265ms):
还能不能继续剪枝?显然可以,对同一个面连续进行超过2次顺时针旋转或逆时针旋转没有任何意义,因为都可以通过更少次数的相反方向旋转实现。
然而,加上这个剪枝并没有什么提高,还能不能更快?

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 int a[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int movp[][];
int nxt[];
int unxt[];
int ans; void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
}
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} inline void move(int *b, int *c, int id) {
int i; for (i=; i<; ++i) c[i] = b[movp[id][i]];
} void dfs(int *a, int n, int pre, int deep) {
int b[]; ans = max(ans, calc(a)); if (n) {
rep(i, , ) {
if ((i^) == pre)
continue;
if (i != pre) {
move(a, b, i);
dfs(b, n-, i, );
} else if (deep < ) {
move(a, b, i);
dfs(b, n-, i, deep+);
}
}
}
} void solve() {
ans = ; dfs(a, n, -, ); printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法六(AC 234ms):
将问题转化,即$n$次旋转可以想象为长度为$n$的$[0,5]$的任意排列,当然这个排列中有些是可以被剪枝的。比如,形如$*F_aF_aF_a*$,或者$*F_aF_b*$。然而,无论魔方初始的配置是什么样的,旋转就是数组中的某些元素交换位置,因此可以预处理进行$[0,7]$次旋转后,数组中元素位置的排列。这个预处理,直接深搜就好了。果然,略微有所优化。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 typedef struct {
char a[];
} node_t; int a[], b[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int movp[][];
int nxt[];
int unxt[];
int ans;
vector<node_t> vc[]; void dfs(node_t nd, int step, int pre, int deep) {
vc[step].pb(nd); node_t d; if (step < ) {
rep(i, , ) {
if ((i^) == pre)
continue;
if (i != pre) {
rep(j, , ) d.a[j] = nd.a[movp[i][j]];
dfs(d, step+, i, );
} else if (deep < ) {
rep(j, , ) d.a[j] = nd.a[movp[i][j]];
dfs(d, step+, i, deep+);
}
}
}
} void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
} node_t nd;
rep(i, , ) nd.a[i] = i;
dfs(nd, , -, );
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} void solve() {
ans = ; rep(i, , n+) {
int sz = SZ(vc[i]);
rep(j, , sz) {
rep(k, , )
b[k] = a[vc[i][j].a[k]]; ans = max(ans, calc(b));
}
} printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法七(AC 171ms):
能不能再快点儿?因为有了预处理,我可以将预处理的深搜变为宽搜,这也是碰到深搜实在没什么可剪枝的优化方式。同样,略微有所提高。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 typedef struct {
char a[];
} node_t; typedef struct {
node_t p;
int pre, deep;
} node; int a[], b[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int movp[][];
int nxt[];
int unxt[];
int ans;
vector<node_t> vc[]; void bfs(); void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
} bfs();
} void bfs() {
queue<node> Q;
node nd, d;
int step = ; rep(i, , ) nd.p.a[i] = i;
nd.pre = -;
nd.deep = ;
Q.push(nd);
vc[step].pb(nd.p); while () {
int sz = SZ(Q);
if (sz== || ++step>)
break;
while (sz--) {
nd = Q.front();
Q.pop();
rep(i, , ) {
if ((i^) == nd.pre)
continue;
if (i != nd.pre) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ;
vc[step].pb(d.p);
Q.push(d); } else if (nd.deep < ) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ;
vc[step].pb(d.p);
Q.push(d);
}
}
}
}
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} void solve() {
ans = ; rep(i, , n+) {
int sz = SZ(vc[i]);
rep(j, , sz) {
rep(k, , )
b[k] = a[vc[i][j].a[k]]; ans = max(ans, calc(b));
}
} printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法八(MLE 109ms):
打表看了一下$[0,7]$共有$98797$种情况,能不能减少?倘若能够减少,也意味着存在着$n_i > n_j$并且进行$n_i$和$n_j$旋转有相同排列的情况,那么$n_i$其实已经不用考虑了。这个排列长度为24,那么怎么判定当前排列是否出现过,并且尽可能节省内存?显然trie树,trie树本来就源自"information retrieval",但是不幸的还是MLE了。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64 typedef struct {
char a[];
} node_t; typedef struct {
node_t p;
int pre, deep;
} node; int a[], b[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int movp[][];
int nxt[];
int unxt[];
int ans;
vector<node_t> vc[]; void bfs(); void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
} bfs();
} typedef struct trie_t {
static const int maxn = ;
static const int rt = ;
int nxt[maxn][];
int l; trie_t() {
l = ;
} inline int newNode() {
assert(l < maxn);
return l++;
} bool Insert(char *s) {
int i = ;
int p = rt;
bool ret = false; while (i < ) {
char& id = s[i++];
if (!nxt[p][id]) {
nxt[p][id] = newNode();
ret = true;
}
p = nxt[p][id];
} return ret;
} } trie_t; trie_t trie; void bfs() {
queue<node> Q;
node nd, d;
int step = ; rep(i, , ) nd.p.a[i] = i;
nd.pre = -;
nd.deep = ;
Q.push(nd);
trie.Insert(nd.p.a);
vc[step].pb(nd.p); while () {
int sz = SZ(Q);
if (sz== || ++step>)
break;
while (sz--) {
nd = Q.front();
Q.pop();
rep(i, , ) {
if ((i^) == nd.pre)
continue;
if (i != nd.pre) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ; } else if (nd.deep < ) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ;
} else {
continue;
} if (trie.Insert(d.p.a)) {
vc[step].pb(d.p);
Q.push(d);
}
}
}
} #ifndef ONLINE_JUDGE
rep(i, , )
printf("%d\n", SZ(vc[i]));
printf("l = %d\n", trie.l);
#endif
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} void solve() {
ans = ; rep(i, , n+) {
int sz = SZ(vc[i]);
rep(j, , sz) {
rep(k, , )
b[k] = a[vc[i][j].a[k]]; ans = max(ans, calc(b));
}
} printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

解法九(AC G++93ms C++109ms):
还是上一个思路,能不能使用别的数据结构代替trie树?可以将长为24的排列想象成字符串,因此适用于字符串的算法都可以采用,所以果断试试哈希(可以与trie对拍一下所有排列)。LCP哈希可以过,ELFHash等都试了一下,过不了。这样,就将$98797$近乎减少了一半,最终仅$44971$种情况,时间上也提高了很多。

 /* 4801 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
#pragma comment(linker,"/STACK:102400000,1024000") #define sti set<int>
#define stpii set<pair<int, int> >
#define mpii map<int,int>
#define vi vector<int>
#define pii pair<int,int>
#define vpii vector<pair<int,int> >
#define rep(i, a, n) for (int i=a;i<n;++i)
#define per(i, a, n) for (int i=n-1;i>=a;--i)
#define clr clear
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1 #define LL __int64
#define ULL unsigned __int64 typedef struct {
char a[];
} node_t; typedef struct {
node_t p;
int pre, deep;
} node; int a[], b[];
int n;
int face[][] = {
{, , , },
{, , , },
{, , , },
{, , , },
{, , , },
{, , , }
}; int movf[][] = {
{,,,, ,,,,,,,},
{,,,, ,,,,,,,},
{,,,, ,,,,,,,}
};
int movp[][];
int nxt[];
int unxt[];
int ans;
vector<node_t> vc[]; void bfs(); void init() {
rep(i, , ) {
nxt[i] = (i+) % ;
unxt[i] = (i-+)%;
} int i, j, k;
for (k=,j=; k<; ++k,j+=) {
int *mf = movf[k];
int *c = movp[j];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[nxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(nxt[i]<<)+]] = mf[(i<<)+];
c[mf[(nxt[i]<<|)+]] = mf[(i<<|)+];
} c = movp[j+];
for (i=; i<; ++i) c[i] = i;
for (i=; i<; ++i)
c[mf[unxt[i]]] = mf[i];
for (i=; i<; ++i) {
c[mf[(unxt[i]<<)+]] = mf[(i<<)+];
c[mf[(unxt[i]<<|)+]] = mf[(i<<|)+];
}
} bfs();
} typedef struct Hash {
static const int MOD = ;
set<ULL> st; void clear() {
st.clr();
} ULL HashCode(const char *s) {
ULL ret = ; rep(i, , )
ret = ret * MOD + s[i]; return ret;
} bool find(const node_t& p) {
ULL h = HashCode(p.a);
if (st.find(h) == st.end()) {
st.insert(h);
return false;
} else {
return true;
}
} } Hash; Hash tb; void bfs() {
queue<node> Q;
node nd, d;
int step = ; rep(i, , ) nd.p.a[i] = i;
nd.pre = -;
nd.deep = ;
Q.push(nd); tb.clr();
tb.find(nd.p);
vc[step].pb(nd.p); while () {
int sz = SZ(Q);
if (sz== || ++step>)
break;
while (sz--) {
nd = Q.front();
Q.pop();
rep(i, , ) {
if ((i^) == nd.pre)
continue;
if (i != nd.pre) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ; } else if (nd.deep < ) {
rep(j, , ) d.p.a[j] = nd.p.a[movp[i][j]];
d.pre = i;
d.deep = ;
} else {
continue;
} if (!tb.find(d.p)) {
vc[step].pb(d.p);
Q.push(d);
}
}
}
}
} int calc(int *b) {
int ret = ; rep(i, , ) {
++ret;
rep(j, , ) {
if (b[face[i][j]] != b[face[i][]]) {
--ret;
break;
}
}
} return ret;
} void solve() {
ans = ; rep(i, , n+) {
int sz = SZ(vc[i]);
rep(j, , sz) {
rep(k, , )
b[k] = a[vc[i][j].a[k]]; ans = max(ans, calc(b));
}
} printf("%d\n", ans);
} int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif init();
while (scanf("%d", &n)!=EOF) {
rep(i, , )
scanf("%d", &a[i]);
solve();
} #ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif return ;
}

93ms就是目前最快的了,因为耗内存,故排第4。后面又尝试了一下写了个内存池代替vector,但是效果并不明显。

这个题目是13年regional的题目,虽然不是特别难,但是可以看到结合多种算法不断进行优化这个过程很重要。
最开始学习算法的时候,基本上没什么思路,很多dp、二分都不会写。碰到TLE基本就要看题解了,现在逐渐可以自己想思路,找优化方式,我觉得这是一个显著的进步,这也是一种提高的方式。我认为算法的学习没什么捷径,深入了解算法的基本思路和证明过程,以及如何对基本算法进行提升很重要。

【HDOJ】4801 Pocket Cube 的几种解法和优化的更多相关文章

  1. HDU 4801 Pocket Cube

    题目链接 去年现场,虎哥1Y的,现在刷刷题,找找状态... 一共6种转法,把3个面放到顶部,左旋和右旋,感觉写的还不错....都写成常数了. #include <stdio.h> #inc ...

  2. 2013区域赛长沙赛区现场赛 K - Pocket Cube

    K - Pocket Cube Time Limit:10000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Su ...

  3. HDU 5292 Pocket Cube 结论题

    Pocket Cube 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5292 Description Pocket Cube is the 2×2× ...

  4. [LeetCode] Remove Element (三种解法)

    Given an array and a value, remove all instances of that value in place and return the new length. T ...

  5. codewars.DNA题目几种解法分析(字符串替换)

    题干: 意思就是字符串替换,"A"与"C"配对,"T"与"G"配对,DNA不为空. 解法一:我的解法,用for循环遍历字 ...

  6. LeetCode算法题-Minimum Distance Between BST Nodes(Java实现-四种解法)

    这是悦乐书的第314次更新,第335篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第183题(顺位题号是783).给定具有根节点值的二叉搜索树(BST),返回树中任何两个 ...

  7. LeetCode算法题-Number Complement(Java实现-五种解法)

    这是悦乐书的第240次更新,第253篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第107题(顺位题号是476).给定正整数,输出其补码数.补充策略是翻转其二进制表示的位 ...

  8. LeetCode算法题-Third Maximum Number(Java实现-四种解法)

    这是悦乐书的第222次更新,第235篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第89题(顺位题号是414).给定非空的整数数组,返回此数组中的第三个最大数字.如果不存 ...

  9. LeetCode算法题-Longest Palindrome(五种解法)

    这是悦乐书的第220次更新,第232篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第87题(顺位题号是409).给定一个由小写或大写字母组成的字符串,找到可以用这些字母构 ...

随机推荐

  1. CPU 时间片 分时 轮转调度

    时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的.如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进 ...

  2. 【学习总结】【多线程】 安全隐患 & 通讯 & 线程的状态

    一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数 ...

  3. Text Template Transformation Toolkit

    Text Template Transformation Toolkit       1.且算简介         笔者以一个英文字母和一个数字取了一个简单的名字.名唤"T4"(名 ...

  4. 被git extensions给坑了,Not owner 解决办法

    我只遇到这一种情况,不能访问git文件主库, 清空以前的历史文件,包括默认配置文件,重新安装一遍git extension,然后设置账号和密码, 在打开bash设置私钥和公钥 把公钥添加到你的git的 ...

  5. Timer 的缺陷

    java.util.Timer计时器有管理任务延迟执行("如1000ms后执行任务")以及周期性执行("如每500ms执行一次该任务").但是,Timer存在一 ...

  6. MyEclipse 安装JRebel进行热部署

    安装环境 版本:myeclipse2015stable2 说明:下面是我已经安装了界面 安装过程 进入市场 出现下面提示,不用管它,点Continue 用关键词搜索 配置 进入JRebel配置中心,配 ...

  7. ffmpeg参数解释

    基本选项: -formats 输出所有可用格式 -f fmt 指定格式(音频或视频格式) -i filename 指定输入文件名,在linux下当然也能指定: 0.0(屏幕录制)或摄像头 -y 覆盖已 ...

  8. CSS reset--重置样式

    在一般我们写一个自己自定义的HTML的时候,我们会清除样式或者说重置样式 重置样式.清除浏览器默认样式,一切全部用自己的设置,并配置适合设计的基础样式 下面给出所有一般需要清除的样式: html,bo ...

  9. Codeforces Round #363 (Div. 2)->C. Vacations

    C. Vacations time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  10. [nowCoder] 两个不等长数组求第K大数

    给定两个有序数组arr1和arr2,在给定一个整数k,返回两个数组的所有数中第K小的数.例如:arr1 = {1,2,3,4,5};arr2 = {3,4,5};K = 1;因为1为所有数中最小的,所 ...