【状压DP】【cofun1376】kings

【cofun1376】kings

Description
用字符矩阵来表示一个8x8的棋盘,’.’表示是空格,’P’表示人质,’K’表示骑士。 每一步,骑士可以移动到他周围的8个方格中的任意一格。如果你移动到的格子中有人质(即’P’),你将俘获他。但不能移到出棋盘或当前是’K’的格子中。 请问最少要移动多少步骑士才能俘获所有的人质。

Input Format
第一行一个整数N(<=5),表示有多少个棋盘。即多组测试数据。 每一组有8行,每行8个字符。字符只有’.’,大写’P’,大写’K’三种字符。’P’和’K’的个数范围都在[1,10]。
Output Format
有N行,每行只一个整数,相应棋盘俘获全部人质所需要的最少步数。

Sample Input
样例1输入
1
.PPPPKP.
……..
……..
……..
……..
……..
……..
……..
样例2输入
2
P……P
……..
……..
……..
…KK…
……..
……..
P……P
…..P.P
..K….P
….K…
..PP…P
…K..KK
……..
K…….
KP.K….
Sample Output
样例1输出
6
样例2输出
20
9


  • 分析:
    一个神奇的DP题目。
    人质数,骑士数 <= 10,考虑状压DP。

    1. 由于骑士可以移动到周围的8个方格,所以可以直接计算出人质与人质,骑士与人质之间的步数。伪代码:
      d[i][j] = max(fabs(x[i] - x[j]), fabs(y[i] - y[j]));
    2. 第一步状压:
      计算从每个人质出发到达其他所有人质需要的最小步数 。
      f1[i][l] = min(f1[i][l], f1[j][l - (1 << i)] + disp[i][j]);
      f1[i][j]:从第i个人质出发到达人质情况为j(把到达人质情况压进二进制数j,0:已到达/ 1:未到达)
    3. 第二步状压:
      计算从每个骑士出发到达所有人质需要的最小步数 。
      f2[i][l] = min(f2[i][l], f1[j][l] + disk[i][j]);
      f2[i][j]:从第i个骑士出发到达人质情况为j(同上)
    4. 第三步状压:
      计算前i个骑士到达所有人质需要的最小步数。
      f3[i][j] = min(f3[i][j], f3[i - 1][j - l] + f2[i][l]);
      f3[i][j]:到第i个骑士为止所到达人质情况为j(同上)

【那会儿琢磨了好久QAQ果然分析一下就很清楚了呢。


  • 代码:
#include <bits/stdc++.h>
 using namespace std;

 struct info{
    int x, y;
 }p[15], k[15];
 int N, i, j, l, np, nk, t, disp[15][15], disk[15][15], f1[15][1 << 15], f2[15][1 << 15], f3[15][1 << 15];
 char c;

 int main()
 {

    for(scanf("%d\n", &N); N; N --)
    {
        np = nk = 0;
        memset(p, 0, sizeof(p));
        memset(k, 0, sizeof(k));
        memset(disp, 0, sizeof(disp));
        memset(disk, 0, sizeof(disk));
        memset(f1, 0x3f, sizeof(f1));
        memset(f2, 0x3f, sizeof(f2));
        memset(f3, 0x3f, sizeof(f3));
        //初始化 
        for(i = 1; i <= 8; i ++)
        {
            for(j = 1; j <= 8; j ++)
            {
                scanf("%c", &c);
                if (c == 'P')
                    p[np].x = i, p[np ++].y = j;
                if (c == 'K')
                    k[nk].x = i, k[nk ++].y = j;
             }
            scanf("\n");
         }
        //读入数据 

        for(i = 0; i < np; i ++)
            for(j = i + 1; j < np; j ++)
                disp[i][j] = disp[j][i] = max(fabs(p[i].x - p[j].x), fabs(p[i].y - p[j].y));
        for(i = 0; i < nk; i ++)
            for(j = 0; j < np; j ++)
                disk[i][j] = max(fabs(k[i].x - p[j].x), fabs(k[i].y - p[j].y));
        //计算P&P之间、K&P之间的距离,即互相到达的最小步数 

        for(i = 0; i < np; i ++)
            f1[i][1 << i] = f1[i][0] = 0;
        for(l = 1; l < (1 << np); l ++)
            for(i = 0; i < np && (1 << i) <= l; i ++)
            if ((1 << i) & l)
                for(j = 0; j < np && (1 << j) <= l; j ++)
                if ((i != j) && ((1 << j) & l))
                    f1[i][l] = min(f1[i][l], f1[j][l - (1 << i)] + disp[i][j]);
        //第一步动规:计算从每个人质出发到达其他所有人质需要的最小步数 
        for(i = 0; i < nk; i ++)
            f2[i][0] = 0;
        for(i = 0; i < nk; i ++)
            for(l = 1; l < (1 << np); l ++)
                for(j = 0; j < np && (1 << j) <= l; j ++)
                if ((1 << j) & l)
                    f2[i][l] = min(f2[i][l], f1[j][l] + disk[i][j]);
        //第二步动规:计算从每个骑士出发到达所有人质需要的最小步数 
        for(i = 0; i < (1 << np); i ++)
            f3[0][i] = f2[0][i];
        for(i = 0; i < nk; i ++)
            f3[i][0] = 0;
        for(i = 1; i < nk; i ++)
            for(j = 1; j < (1 << np); j ++)
                for(l = 0; l <= j; l ++)
                if ((l & j) == l)
                    f3[i][j] = min(f3[i][j], f3[i - 1][j - l] + f2[i][l]);
        //第三步动规:计算前i个骑士到达所有人质需要的最小步数

        printf("%d\n", f3[nk - 1][(1 << np) - 1]);
        //输出答案 
     }

    return 0;

 }

  • 打完这题后也调了蛮久,自己写了个小总结,顺便放上来:
    总结:
    1. 注意数组范围是从1还是0开始以及结束是否为个数减一。
    2. 动规按照方程顺序循环,不要混乱。
    3. 注意初始化 ,比如这题初始化二进制0,状压从1开始循环。
    4. 保持思路清晰。

食堂的鸡腿儿真的很治愈人哇!!太好吃辣!
= =打完第二题后忍不住犯困了一会,果然我不适合修仙TUT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值