【HDOJ】4801 Pocket Cube 的几种解法和优化
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 的几种解法和优化的更多相关文章
- HDU 4801 Pocket Cube
题目链接 去年现场,虎哥1Y的,现在刷刷题,找找状态... 一共6种转法,把3个面放到顶部,左旋和右旋,感觉写的还不错....都写成常数了. #include <stdio.h> #inc ...
- 2013区域赛长沙赛区现场赛 K - Pocket Cube
K - Pocket Cube Time Limit:10000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Su ...
- HDU 5292 Pocket Cube 结论题
Pocket Cube 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5292 Description Pocket Cube is the 2×2× ...
- [LeetCode] Remove Element (三种解法)
Given an array and a value, remove all instances of that value in place and return the new length. T ...
- codewars.DNA题目几种解法分析(字符串替换)
题干: 意思就是字符串替换,"A"与"C"配对,"T"与"G"配对,DNA不为空. 解法一:我的解法,用for循环遍历字 ...
- LeetCode算法题-Minimum Distance Between BST Nodes(Java实现-四种解法)
这是悦乐书的第314次更新,第335篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第183题(顺位题号是783).给定具有根节点值的二叉搜索树(BST),返回树中任何两个 ...
- LeetCode算法题-Number Complement(Java实现-五种解法)
这是悦乐书的第240次更新,第253篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第107题(顺位题号是476).给定正整数,输出其补码数.补充策略是翻转其二进制表示的位 ...
- LeetCode算法题-Third Maximum Number(Java实现-四种解法)
这是悦乐书的第222次更新,第235篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第89题(顺位题号是414).给定非空的整数数组,返回此数组中的第三个最大数字.如果不存 ...
- LeetCode算法题-Longest Palindrome(五种解法)
这是悦乐书的第220次更新,第232篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第87题(顺位题号是409).给定一个由小写或大写字母组成的字符串,找到可以用这些字母构 ...
随机推荐
- cocos2d下,优秀骨骼spine的换装思路
语文老师说,文章要有个好开头!!! 最近正在引入spine骨骼代替dragon bone骨骼,既然要替代,那么原先在dragon bone上的一些额外需求,不管dragon bone上能不能实现,都应 ...
- cocos2dx中的背景图层CCLayerColor和渐变图层CCLayerGradient
1.CCLayerColor是专门用来处理背景颜色的图层,它继承自CCLayer,可以用来设置图层的背景颜色,因为CCLayer默认是透明色的,即无颜色的 2.CCLayerGradient是用来显示 ...
- 如何编写好的jQuery代码
本文就是自己看,如果您不小心进到了这里,请看源处,是这个作者翻译的:http://blog.sae.sina.com.cn/archives/4157 讨论jQuery和javascript性能的文章 ...
- win7 telnet命令无法使用
很多做网络测试的同学发现安装win7后,无法使用telnet命令了,提示“telnet不是内部或外部命令,也不是可运行的程序”,但是很需要在win7中使用telnet工具,怎么办? 首先你要要确认你的 ...
- 《C++Primer》复习——with C++11 [4]
考虑到STL的掌握主要靠的是练习,所以对于STL这部分,我把书中的练习都做一遍,加深印象.这些练习是第9.10.11.17章的,分别是顺序容器.泛型算法和关联容器等. ——10月22日 /*----- ...
- c++ 格式化printf
类型为uint64_t的变量,使用printf进行打印时,需要区分操作系统: 64位系统:使用%ld 32位系统:使用%llu #include<stdio.h>#include < ...
- PHP 打印函数之 print print_r
print 说明 int print ( string $arg ) 输出 arg print 实际上不是一个函数(它是一个语言结构),因此你可以不必使用圆括号来括起它的参数列表 参数 arg:输入数 ...
- HDU 3397 Sequence operation (区间合并,操作比较多)
费了我一天半的时间,到处debug,后来才发现,主要是建树的时候只在叶子节点对lazy1和lazy2进行初始化了,父节点都没初始化...晕. 具体见代码吧. #include <iostream ...
- HDU 1385 Minimum Transport Cost (最短路,并输出路径)
题意:给你n个城市,一些城市之间会有一些道路,有边权.并且每个城市都会有一些费用. 然后你一些起点和终点,问你从起点到终点最少需要多少路途. 除了起点和终点,最短路的图中的每个城市的费用都要加上. 思 ...
- 2013 Multi-University Training Contest 1 3-idiots
解题报告: 记录 A_i 为长度为 i 的树枝的数量,并让 A 对它本身做 FFT,得到任意选两个树枝能得到的各个和的数量.枚举第三边, 计算出所有两边之和大于第三条边的方案数,并把前两条边包含最长边 ...