• Kings(状压DP)


    Description

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

    Input Format

    第一行一个整数N(<=5),表示有多少个棋盘。即多组测试数据。每一组有8行,每行8个字符。字符只有'.',大写'P',大写'K'三种字符。'P'和'K'的个数范围都在[1,10]。

    Output Format

    有N行,每行只一个整数,相应棋盘俘获全部人质所需要的最少步数。

    Sample Input

    2
    P......P
    ........
    ........
    ........
    ...KK...
    ........
    ........
    P......P
    .....P.P
    ..K....P
    ....K...
    ..PP...P
    ...K..KK
    ........
    K.......
    KP.K....

    Sample Output

    20
    9

    Solution

    多亏参考了省队队长的代码,%yh,

    可以发现骑士和人质数量极小,考虑状压DP。

    虽然骑士有好多个,实际上他们不影响,可以先分别做DP,不妨让F[k][i][S]表示第k个骑士在第i个点且俘获状态为S的最少步数,易得F[k][j][S|1<<(j-1)]=min{f[k][i][S]+ptp[i][j]},其中ptp[i][j]表示人质i到人质j的最少步数

    这里有个关键的地方就是骑士可以向8个方向移动,所以2点之间最少步数应为max(|x1-x2|,|y1-y2|)

    然后记录每个骑士i对于状态S的最少步数,我的代码是用F[k][0][S]表示

    接下来在做一次DP,用G[i][S]表示前i个骑士对于状态S的最少步数,

    则G[i][S]=min{G[i-1][S^S2]+F[i][S2]},1<=S2<=最终状态,且(S | S2) == S,答案就很明显了

    Code

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #define N 12
    using namespace std;
    
    struct info {
    	int x, y;
    } k[N], p[N];
    int T, knum, pnum, dis[N][N], f[N][N][1 << N], ptp[N][N], ans[N][1 << N];
    
    inline void Init() {
    	memset(dis, 0, sizeof(dis));
    	knum = pnum = 0;
    	for (int i = 1; i <= 8; ++i)
    		for (int j = 1; j <= 8; ++j) {
    			char ch = getchar();
    			while (ch != '.' && ch != 'K' && ch != 'P') ch = getchar();
    			if (ch == 'K') k[++knum] = (info) {i, j};
    			if (ch == 'P') p[++pnum] = (info) {i, j};
    		}
    
    	for (int i = 1; i <= knum; ++i)
    		for (int j = 1; j <= pnum; ++j) {
    			int x1 = k[i].x, y1 = k[i].y, x2 = p[j].x, y2 = p[j].y;
    			dis[i][j] = max(fabs(x1 - x2), fabs(y1 - y2));
    		}
    	for (int i = 1; i <= pnum; ++i)
    		for (int j = i + 1; j <= pnum; ++j) {
    			int x1 = p[i].x, y1 = p[i].y, x2 = p[j].x, y2 = p[j].y;
    			ptp[i][j] = ptp[j][i] = max(fabs(x1 - x2), fabs(y1 - y2));
    		}
    }
    
    inline void DP(int k) {
    	for (int i = 1; i <= pnum; ++i)
    		f[k][i][1 << (i - 1)] = dis[k][i];
    	for (int S = 1; S < (1 << pnum); ++S)
    		for (int i = 1; i <= pnum; ++i)
    			if (S & (1 << (i - 1)))
    				for (int j = 1; j <= pnum; ++j)
    					if (!(S & (1 << (j - 1))))
    						f[k][j][S | (1 << (j - 1))] = min(f[k][j][S | (1 << (j - 1))], f[k][i][S] + ptp[i][j]);
    }
    
    int main() {
    	scanf("%d", &T);
    	while (T--) {
    		Init();
    
    		memset(f, 0x3f, sizeof(f));
    		for (int k = 1; k <= knum; ++k) {
    			DP(k);
    			for (int S = 1; S < (1 << pnum); ++S)
    				for (int i = 1; i <= pnum; ++i)
    					f[k][0][S] = min(f[k][0][S], f[k][i][S]);
    		}
    
    		memset(ans, 0x3f, sizeof(ans));
    		ans[0][0] = 0;
    		for (int i = 1; i <= knum; ++i) {
    			ans[i][0] = 0;
    			for (int S = 1; S < (1 << pnum); ++S) {
    				ans[i][S] = ans[i - 1][S];
    				for (int g = 1; g < (1 << pnum); ++g) {
    					if ((S | g) != S) continue;
    					ans[i][S] = min(ans[i][S], ans[i - 1][S ^ g] + f[i][0][g]);
    				}
    			}
    		}
    		printf("%d
    ", ans[knum][(1 << pnum) - 1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    精读大道至简01
    mysql查询语句出现sending data耗时解决
    定位线上问题
    docker命令
    .net core2学习笔记
    centos下安装色彩scrapy
    mysql中id值被重置的情况
    MySql中的SHOW INDEX 查出的结果列代表的意义
    ELK安装
    使用PowerDesigner进行面向对象分析与UML建模(转)
  • 原文地址:https://www.cnblogs.com/void-f/p/7656521.html
Copyright © 2020-2023  润新知