2020年9月22日 - 课程

2020年9月22日 - 课程

image.png

image.png

image.png

集训安排

组别 上午 下午
J J模拟赛 初赛讲解
S S模拟赛 专题训练
J+S J模拟赛 S模拟赛

最短Hamilton路径

给定一张 \(n\) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数\(n\)

接下来\(n\)行每行\(n\)个整数,其中第\(i\)行第\(j\)个整数表示点\(i\)\(j\)的距离(记为a[i,j])。

对于任意的\(x,y,z\),数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。

输出格式

输出一个整数,表示最短Hamilton路径的长度。

数据范围

\(1 \le n \le 20\)
\(0 \le a[i,j] \le 10^7\)

输入样例:

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例:

18

解析

  1. i代表路径的状态,j代表经过第几个点
    F[1,0]初始状态
    F[2n-1,n-1]结束

    \[F\left[i,j\right] = \min{(F\left[\ i \operatorname{xor}\ (1<<j),k\ \right]+\operatorname{weight}_{k,j}\ )} \]

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[1 << 20][20], weight[20][20], n;

int hamilton(int n, int weight[20][20]) {
	memset(f, 0x3f, sizeof(f));
	f[1][0] = 0;
	for (int i = 1; i < 1 << n; i++)
        //dp的阶段,从二进制角度来看,从经过(出发)第一个i,到所有点都经过
        //j是刚刚定义的点(目前的位置)
        for (int j = 0; j < n; j++) if (i >> j & 1)//如果在i表示的状态中,j点的位置恰好为1
			for (int k = 0; k < n; k++) if ((i^1<<j) >> k & 1)
				f[i][j] = min(f[i][j], f[i^1<<j][k]+weight[k][j]);
    			//以为你j是刚经过的,此处使用取反代表j的位置没有经过
    			//i的状态符合:
    			//1. j点位置是0
    			//2. k点位置为1
    			//upd答案	
	return f[(1 << n) - 1][n - 1];
    //达到便捷,第一维度代表状态全1
    //第二维度代表所有点都经过了
}
//所有的状态 i,所有的状态里面符合条件的是j,k
//j位置上1,i状态中,j位置是1
//之前的状态j=0,k=1
int main() {
	cin >> n;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			scanf("%d", &weight[i][j]);
	cout << hamilton(n, weight) << endl;
}

291. 蒙德里安的梦想

求把\(N*M\)的棋盘分割成若干个\(1*2\)的的长方形,有多少种方案。

例如当\(N=2,M=4\)时,共有5种方案。当\(N=2,M=3\)时,共有3种方案。

如下图所示:

2411_1.jpg

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数N和M。

当输入用例\(N=0,M=0\)时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

数据范围

\(1≤N,M≤11\)

输入样例:

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

输出样例:

1
0
1
2
3
5
144
51205

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int n, m;
long long f[12][1 << 11];
bool in_s[1 << 11];

int main()  {
	while (cin >> n >> m && n) {//读入
		for (int i = 0; i < 1 << m; i++) {
            //集中检查合法的整数,连续的0的长度是偶数
			bool cnt = 0, has_odd = 0;
			for (int j = 0; j < m; j++)//枚举整数i的二进制形式的每一位
				if (i >> j & 1) has_odd |= cnt, cnt = 0;
				else cnt ^= 1;
            	//hasodd记录曾经出现过的cnt
            	//反转奇偶状态
			in_s[i] = has_odd | cnt ? 0 : 1;//只有hasodd和cnt都是0才符合条件
		}
		f[0][0] = 1;
		for (int i = 1; i <= n; i++)
			for (int j = 0; j < 1 << m; j++) {
				f[i][j] = 0;
				for (int k = 0; k < 1 << m; k++)
					if ((j&k) == 0 && in_s[j | k])
						f[i][j] += f[i - 1][k];
			}
		cout << f[n][0] << endl;
	}
}

292 炮兵阵地

司令部的将军们打算在\(N*M\)的网格地图上部署他们的炮兵部队。一个$N*M的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

1185_1.jpg

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。

从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示N和M;

接下来的N行,每一行含有连续的M个字符(‘P’或者’H’),中间没有空格。按顺序表示地图中每一行的数据。

输出格式

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

数据范围

\(N≤100,M≤10\)

输入样例:

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

输出样例:

6

Code

/**
 * @Author:      H S-J
 * @DateTime:    2018-04-18 20:10:21
 * @Description: 
 */
#include <iostream>
#include <cstring>
using namespace std;
#define MST(a, b) memset(a, b, sizeof(a));
#define CLR(a) MST(a, 0);
#define rep(x, y, z) for (int x = y; x < z; ++x)
const int INF = 0x3f3f3f3f;
int dp[101][77][77];
int sg[101];
int n, m, idx;
int s[77]; //合法摆放的集合
int cnt0[77]; //合法摆放方案的具体摆放个数, 即二进制下1的个数
int get_one(int x) {
	int cnt = 0;
	while(x) x &= (x-1), ++cnt;
	return cnt;
}
bool ok(int x) {
	// 相邻两个P之间要有两个H
	if(x & (x << 1)) return false;
	if(x & (x << 2)) return false;
	return true;
}
void init() {
	idx = 0;
	int end = 1 << m;
	rep(i, 0, end) if(ok(i)) {
		// s保存合法方案的集合
		s[idx] = i;
		// cnt0保存合法方案的摆放个数, 二进制位1的个数
		cnt0[idx++] = get_one(i);
	}
}
bool valid(int i, int x) {
	if(sg[i] & x) return false;
	return true;
}
int solve() {
	int ans = 0;
	MST(dp, -1);
	dp[0][0][0] = 0;
	rep(j, 0, idx) if(valid(1, s[j])) {
		dp[1][j][0] = cnt0[j];
		// 考虑n==1情况
		ans = max(ans, dp[1][j][0]);
	}
	rep(i, 2, n+1) {
		// valid()函数判断, 第i行, 用方案s[j]是否合法
		rep(j, 0, idx) if(valid(i, s[j])) {
			// i行跟i-1行的方案, 满足, 互相炸不到对方
			rep(k, 0, idx) if(valid(i-1, s[k]) && (s[j]&s[k])==0) {
				int last = 0;
				// i-2行同上
				rep(l, 0, idx) if(dp[i-1][k][l] != -1 && (s[l]&s[j])==0 && valid(i-2, s[l])) {
					last = max(last, dp[i-1][k][l]);
				}
				dp[i][j][k] = max(dp[i][j][k], last + cnt0[j]);
				if(i == n) ans = max(ans, dp[i][j][k]);
			}
		}
	}
	return ans;
}
int main(int argc, char const *argv[]) {
	ios::sync_with_stdio(0);cin.tie(0);
	cin >> n >> m;
	rep(i, 1, n+1) rep(j, 0, m) {
		char tmp; cin >> tmp;
		if(tmp == 'H') sg[i] |= (1 << (m-1-j));
	}
	init();
	cout << solve() << endl;
	return 0;
}
posted @ 2020-09-22 20:35  SweepyZhou  阅读(173)  评论(0)    收藏  举报