2019徐州网络赛 L. Dice(找规律+状压DP)

题目链接

题目大意

有一颗骰子,一开始在(0,0)位置,点数1朝上。骰子访问了点(x,y),当且仅当骰子处于点(x,y)且点数1朝上。给你n个点,求骰子经过这n个点需要的最小翻滚次数。 1 ≤ n ≤ 16 1\le n\le16 1n16

解题思路:

看到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

  1. x = 1且y = 1:答案为6
  2. x<=1且y不是4的倍数:答案为x+y+2
  3. x<=1且y是4的倍数:答案为x+y
  4. 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}};
//0~5分别表示上,前,右,后,左,下
int dis[100][100][6];//dis[i][j][k]表示在(i,j)且状态为k
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()
{
//    bfs();//小范围数据打表
//    for(int i = 0; i < 20; ++i){
//        for(int j = i; j < 20; ++j) cout<<"i:"<
//    }
    int T;cin>>T;
    while(T--){
        init();sol();
    }
}

你可能感兴趣的:(dp)