UVALive 6669 (LA 6669) Dragon’s Cruller (康托展开 + 最短路)

题目大意:

就是一个3*3矩阵的游戏, 现在一共有9个格子, 然后有8个格子被8种方块填充, 另外一个是空格, 现在给出了一个表分别表示A~I位置能到达的位置和需要的代价

需要的代价只有两种

每次可以移动一个块到空位置

为从一个状态最小需要多少代价变成另外一种


大致思路:

首先对于一个当前的游戏状态,用康托展开来记录当前状态,即当前状态可以表示成a1, a2, a3, ..., a9是一个0~8的排列

那么这个状态可以映射成 a1*0! + a2*1! + .. + a9*8!

其中ai表示第i个数的前面比ai小的有几个

那么可以将所有的9!种状态映射到数字0~9! - 1

那么每个点相连的边数是4, 权值有两种

这样建好一个图之后,跑一下最短路即可

我写的spfa...貌似堆优化的dijkstra要快一点

注意建图的边要预处理, 不能每次都完全重建, 不然容易超时


代码如下:

Result  :  Accepted     Memory  :  ? KB     Time  :  2176 ms

/*
 * Author: Gatevin
 * Created Time:  2015/10/31 13:46:57
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

int a[10];
int ch, cv;
int st, ed;
int fac[10];
int b[10], c[10];

#define maxn (1*2*3*4*5*6*7*8*9+10)

struct Edge
{
    int to, nex, w, typ;
    Edge(){}
    Edge(int _to, int _nex, int _w, int _typ)
    {
        to = _to, nex = _nex, w = _w, typ = _typ;
    }
};
Edge edge[maxn*4*2 + 2];

int head[maxn];
int E;

void add_Edge(int u, int v, int w, int typ)
{
    edge[++E] = Edge(v, head[u], w, typ);
    head[u] = E;
}

int change[9][4] =//转移
{
    {1, 8, 6, 3},
    {2, 0, 7, 4},
    {3, 1, 8, 5},
    {4, 2, 0, 6},
    {5, 3, 1, 7},
    {6, 4, 2, 8},
    {7, 5, 3, 0},
    {8, 6, 4, 1},
    {0, 7, 5, 2}
};

void build()
{
    ch = cv = 0;
    memset(head, -1, sizeof(head));
    E = 0;
    int start = 0;
    int end = 0;
    for(int i = 0; i <= 8; i++)
        end += i*fac[i];
    for(int i = start; i <= end; i++)//解Cantor展开
    {
        int ti = i;
        int emp;
        memset(a, 0, sizeof(a));
        for(int j = 8; j >= 0; j--)
        {
            int cnt = ti / fac[j];
            for(int p = 0; p <= 8; p++)
            {
                //if(a[i] == 0) cnt--;
                if(cnt == 0 && a[p] == 0)
                {
                    b[j] = p;
                    a[p] = 1;
                    break;
                }
                else if(a[p] == 0) cnt--;
            }
            ti %= fac[j];
            if(b[j] == 0) emp = j;
        }
        for(int k = 0; k < 4; k++)//计算目标状态的Cantor展开的值
        {
            int swa = change[emp][k];
            for(int g = 0; g <= 8; g++)
                c[g] = b[g];
            swap(c[emp], c[swa]);
            int to = 0;
            for(int g = 0; g <= 8; g++)
            {
                int cnt = 0;
                for(int mabi = 0; mabi < g; mabi++)
                    if(c[mabi] < c[g]) cnt++;
                to += cnt*fac[g];
            }
            add_Edge(to, i, (k > 1) ? cv : ch, (k > 1));
        }
    }
}

int Cantor()
{
    int ret = 0;
    for(int i = 0; i < 9; i++)
    {
        int cnt = 0;
        for(int j = 0; j < i; j++)
            if(a[i] > a[j]) cnt++;
        ret += cnt*fac[i];
    }
    return ret;
}

bool vis[maxn];
int dis[maxn];
const int inf = 1e9;

void solve()//spfa
{
    memset(vis, 0, sizeof(vis));
    fill(dis, dis + fac[9] + 1, inf);
    dis[st] = 0;
    queue<int> Q;
    Q.push(st);
    vis[st] = 1;
    while(!Q.empty())
    {
        int now = Q.front();
        Q.pop();
        vis[now] = 0;
        for(int i = head[now]; i + 1; i = edge[i].nex)
        {
            int nex = edge[i].to;
            if(dis[nex] > dis[now] + edge[i].w)
            {
                dis[nex] = dis[now] + edge[i].w;
                if(!vis[nex])
                    Q.push(nex), vis[nex] = 1;
            }
        }
    }
    printf("%d\n", dis[ed]);
}

int main()
{
    fac[0] = fac[1] = 1;
    for(int i = 2; i < 10; i++) fac[i] = fac[i - 1]*i;
    build();
    while(scanf("%d %d", &ch, &cv), ch || cv)
    {
        for(int i = 0; i < 9; i++)
            scanf("%d", &a[i]);
        st = Cantor();
        for(int i = 0; i < 9; i++)
            scanf("%d", &a[i]);
        ed = Cantor();
        //build();
        for(int i = 1; i <= E; i++)
            if(edge[i].typ == 1)
                edge[i].w = cv;
            else edge[i].w = ch;
        solve();
    }
    return 0;
}
/*
4 9
6 3 0
8 1 2
4 5 7
6 3 0
8 1 2
4 5 7
31 31
4 3 6
0 1 5
8 2 7
0 3 6
4 1 5
8 2 7
92 4
1 5 3
4 0 7
8 2 6
1 5 0
4 7 3
8 2 6
12 28
3 4 5
0 2 6
7 1 8
5 7 1
8 6 2
0 3 4
0 0
*/

/*
0
31
96
312
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值