题目链接
题目大意
有一颗骰子,一开始在(0,0)位置,点数1朝上。骰子访问了点(x,y),当且仅当骰子处于点(x,y)且点数1朝上。给你n个点,求骰子经过这n个点需要的最小翻滚次数。 1 ≤ n ≤ 16 1\le n\le16 1≤n≤16
解题思路:
看到n这么小,第一时间想到状压DP, d p [ m a s k ] [ u ] dp[mask][u] dp[mask][u]表示经过了点集mask且最后一个点为u所需要的最小花费。转移为 d p [ m a s k ] [ v ] = d p [ m a s k ∧ ( 1 < < v ) ] + d i s ( u , v ) dp[mask][v]=dp[mask\wedge(1<<v)]+dis(u,v) dp[mask][v]=dp[mask∧(1<<v)]+dis(u,v), d i s ( u , v ) dis(u,v) dis(u,v)为从点u到点v需要的最小翻滚次数。
怎么求这个翻滚次数呢?赛场上我做了一个骰子翻滚着玩,但是手笨没能玩出规律。其实想找规律的话这里是可以在小范围打个表找规律的。用 d i s [ x ] [ y ] [ s t a t e ] dis[x][y][state] dis[x][y][state]表示骰子在点(x,y),用state表示当前骰子的1的朝向(上下左右前后),然后bfs一下可以求得最小距离的表。
打出表来之后很容易发现,有以下几种情况(因为x和y交换不影响答案,不妨让x
- x = 1且y = 1:答案为6
- x<=1且y不是4的倍数:答案为x+y+2
- x<=1且y是4的倍数:答案为x+y
- x > 1: 答案为x+y
求得距离之后就可以开始dp了。这个故事告诉我们,遇事不决先打表(雾)
ac代码(附打表代码)
#include
#define ll long long
#define lowbit(x) (x&(-x))
using namespace std;
const int inf = 0x3f3f3f3f;
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int nxt_state[6][4] = {{1,3,4,2},{5,0,1,1},{2,2,0,5},{0,5,3,3},{4,4,5,0},{3,1,2,4}};
int dis[100][100][6];
struct node{int x, y, state; node(int a = 0, int b = 0, int c = 0):x(a),y(b),state(c){}};
bool in(int x, int y){
if(x < 0 || x >= 100) return false;
if(y < 0 || y >= 100) return false;return true;
}
queue<node> q;
void bfs()
{
memset(dis, -1, sizeof dis);
dis[0][0][0] = 0;
q.push(node(0,0,0));
while(q.size()){
node temp = q.front(); q.pop();
int x = temp.x, y = temp.y, state = temp.state;
for(int i = 0; i < 4; ++i){
int xx = x + dx[i], yy = y + dy[i], ss = nxt_state[state][i];
if(in(xx,yy) && dis[xx][yy][ss] == -1){
dis[xx][yy][ss] = dis[x][y][state] + 1;
q.push(node(xx,yy,ss));
}
}
}
}
int n;
int f(int x, int y){
x = abs(x), y = abs(y);
if(x > y) swap(x, y);
if(x <= 1){
if(x == 1 && y == 1) return 6;
if(y%4 == 0) return x + y;
return x+y+2;
}
return x+y;
}
int x[20], y[20];
int dp[1<<20][20];
void init()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i) scanf("%d%d", &x[i], &y[i]);
}
void sol(){
for(int i = 0; i < n; ++i) dp[1<<i][i] = f(x[i], y[i]);
for(int mask = 1; mask < (1<<n); ++mask){
if(mask == lowbit(mask)) continue;
for(int v = 0; v < n; ++v){
if(!((mask>>v)&1)) continue;
dp[mask][v] = inf;
for(int u = 0; u < n; ++u){
if( !((mask>>u)&1) ) continue;
if(u == v) continue;
dp[mask][v] = min(dp[mask^(1<<v)][u] + f(x[u] - x[v], y[u] - y[v]), dp[mask][v]);
}
}
}
int ans = dp[(1<<n)-1][0];
for(int i = 1; i < n; ++i) ans = min(ans, dp[(1<<n)-1][i]);
cout<<ans<<endl;
}
int main()
{