洛谷P3956 [NOIp2017]棋盘

来源:互联网 发布:大数据世界txt下载 编辑:程序博客网 时间:2024/04/20 08:01

分析

  • 在考场上乱搞了个DP……
  • 首先我们可以知道,这里的施展魔法实际上就相当于从一个有颜色的方块走到另一个与其距离为2的有颜色的方块
  • 那么由于这题盲目地往下和往右走显然没有正确性,我一开始的想法是设f[k][i][j]表示走了k步到方块(i,j)所花费的最少金币,转移就是从一个点走到另一个点加上花费的金币,不过这样的时空复杂度都有些难以承受……
  • 但我们可以发现:所有的“行走”显然都是在有颜色的方块之间进行
  • 于是我们可以就给所有有颜色的方块编号,设f[k][i]表示走了k步到编号为i的方块所花费的最少金币,接着预先把所有距离为1和2的成对有颜色的方块存下来,直接在转移中使用即可
  • 这样f数组不用开很大,时空复杂度就比较优秀了
  • 出了考场后才发现建个图跑个最短路就没了……

代码

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int Maxn = 0x3f3f3f3f;const int N = 1005;const int dx[] = {1, -1, 0, 0},          dy[] = {0, 0, 1, -1};int f[N][N], a[N][N], x[N], y[N], z[N];int n, m, pN, qN, Ans = Maxn;struct Link{    int x, y, z;}p[N << 4], q[N << 4];inline void CkMin(int &x, int y){    if (x > y) x = y;}inline int Abs(int x){    return x < 0 ? -x : x;}inline int Tag(int x, int y){    return x != y ? 1 : 0;}inline void Init(){    for (int i = 1; i <= n; ++i)        for (int j = 1; j <= n; ++j)        if (i != j)        {            int tmp = Abs(x[i] - x[j]) + Abs(y[i] - y[j]);            if (tmp == 1)             {                ++pN; p[pN].x = i; p[pN].y = j;                p[pN].z = Tag(a[x[i]][y[i]], a[x[j]][y[j]]);            }            else if (tmp == 2)            {                ++qN;                int dx = a[x[i]][y[i]], dy = a[x[j]][y[j]];                q[qN].x = i; q[qN].y = j;                q[qN].z = Tag(dx, 0) + Tag(dy, 0) + 2;                CkMin(q[qN].z, Tag(dx, 1) + Tag(dy, 1) + 2);            }        }}int main(){//  freopen("chess.in", "r", stdin);//  freopen("chess.out", "w", stdout);    scanf("%d%d", &m, &n);    for (int i = 1; i <= m; ++i)        for (int j = 1; j <= m; ++j)            a[i][j] = -1;    for (int i = 1; i <= n; ++i)    {        scanf("%d%d%d", &x[i], &y[i], &z[i]);         a[x[i]][y[i]] = z[i];    }    if (a[1][1] == -1)    {        puts("-1");        fclose(stdin); fclose(stdout);        return 0;    }    Init();    memset(f, Maxn, sizeof(f));    for (int i = 1; i <= n; ++i)        if (x[i] == 1 && y[i] == 1)        {            f[1][i] = 0;            break;        }    int lim = 1000; CkMin(lim, m * m);    for (int k = 1; k < lim; ++k)    {        for (int i = 1; i <= pN; ++i)        if (f[k][p[i].y] != Maxn)             CkMin(f[k + 1][p[i].x], f[k][p[i].y] + p[i].z);        for (int i = 1; i <= qN; ++i)        if (f[k][q[i].y] != Maxn)            for (int j = 0; j <= 1; ++j)                CkMin(f[k + 2][q[i].x], f[k][q[i].y] + q[i].z);    }    if (a[m][m] != -1)    {        for (int i = 1; i <= n; ++i)            if (x[i] == m && y[i] == m)            {                   for (int k = m + m - 1; k <= lim; ++k)                    CkMin(Ans, f[k][i]);                break;            }    }    else //如果方块(m,m)无色要另外处理     {        for (int i = 1; i <= n; ++i)            if (x[i] + y[i] == m + m - 1)                for (int k = m + m - 2; k <= lim; ++k)                    CkMin(Ans, f[k][i] + 2);    }    printf("%d\n", Ans == Maxn ? -1 : Ans);//  fclose(stdin); fclose(stdout);    return 0;}