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. Swift和OC,是编译型语言、解释性语言、运行时语言

    首先需要明确的一点是,什么是编译型语言和解释性语言 编译型语言,就是在其执行过程中需要先将其经过编译成机器码来给计算机识别的,其执行效率就会比较高这个是显而易见的,常见比如:C.C++ 而解释型语言, ...

  2. C#中读取二维数组每位的长度

    C#中的二维数组,如int[,] A=new int[a,b];则 a=A.GetLength(0);即可获得二维数组中第一维的长度. b=A.GetLength(1);即可获得二维数组中第二维的长度 ...

  3. Ruby 语法快速入门

    作用域:指的是类,模块,方法 常量:无需指定类型,全大写 FANS = 100 puts "We have" + FANS.to_s + "fans" 变量 局 ...

  4. UML类图关系-转

    1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法. 在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设计模式内的类关系来 ...

  5. md RAID

    md: mdadm: 将任何块设备做成RAID 模式化的命令:1.创建模式 -C 专用选项: -l: 级别 -n #: 设备个数 -a {yes|no}: 是否自动为其创建设备文件 -c: CHUNK ...

  6. 【转载】hadoop的版本问题

    免责声明:     本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系博主进行删除.     原文作者:阿笨猫      原文地址:http://www.cnblogs.com/xu ...

  7. 【POJ】【1061】/【BZOJ】【1477】青蛙的约会

    扩展欧几里德 根据题意列出不定方程: (x+m*T)-(y+n*T)=k*L; //T表示跳了T次,由于是环,可能追了多圈,所以结果应为k*L 化简得  T(m-n)-kL=y-x; 这就成了我们熟悉 ...

  8. [nowCoder] 子数组最大乘积

    给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积.例如arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以 ...

  9. soap消息机制 讲解

    SOAP(Simple Object Access Protocol,简单对象访问协议)作为一种信息交互协议在分布式应用中非常广泛,如WebService.在使用.Net开发WebService时候, ...

  10. [shell编程]初识sed和gawk

    一.sed编辑器       shell脚本最常见的用途就是处理文本文件,sed和gawk能够极大的简化需要进行的数据处理任务.sed编辑器是流编辑器,跟普通交互式文本编辑器(如vim)不同.流编辑器 ...