2025/5/4集训(上午)

树形dp:
给你一棵树,存储他,然后dp(没错,就这么简单)
比如:
找到树的重心:找到一个点,让这个点到其他所有点的距离最小(点的个数n<=10^5)
f[i]:以i为根的子树的信息(此处为:以i为根的所有点到i的距离之和)
对于叶子节点,f[i] = 0;
那么怎么转移呢?
从下到上合并儿子的信息,进行整合.
状压dp:
状态压缩:把数组变成数
例如:\({1,2,3,1,0}\)可以写成\(12310\)
主播主播,你的直接映射确实很简单,但还是太弱了(你敢想象如果有\(100\)个数会是什么样子吗),有没有什么更强势的方法推荐一下?
有的兄弟,有的.当成其他进制就更简单了:
\(1*4^4+2*4^3...\)
$=300 $
例题:TSP(旅行者):
给你n个点,把所有点走一遍,让你走过的路程最小(怎么走都行):
方法1:用数组存有没有走过,比如:
点: _ _ _6 5 4 3 2 1
走没走: T F F T T F
变成二进制:100110,状压成功.

double f[1<<maxn][maxn];//f[s][i]代表最小路径s代表哪些点已经走过的状压数组,i表示走到哪个点了 
int main(){//时间复杂度:O(n^2*2^n),n <= 18
		   //阶乘才O(n!)啊,能在n <= 11内不T,状压dp恐怖如斯 
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> x[i] >> y[i];
	for (int i = 0; i < (1<<n); i++)
		for (int j = 0; j < n; j++)
			f[i][j] = 1e+20;
	f[1][0] = 0;
	for (int i = 1; i < (1<<n); i++)
		for (int j = 0; j < n; j++){//从f[i][j]这个状态向外转移 
			if (i>>j&1){//i的二进制第j位需要是1,才是合法状态,代表j点走过
			 	for (int k = 0; k < n; k++)
				 	if (!(i>>k&1)) {//k点没走过,可以冲
					 	f[i|(1<<k)][k] = min(f[i|(1<<k)][k], f[i][j] + dis(j, k));//dis代表j到k的距离,为了省事先不写 
					 }
			}
		}
} 

例题2:k国王
第一种方法:一列一列放
f[i][j][s];//1~i行,放了j个,s的位置有国王
方法1:

 #include <bits/stdc++.h>
using namespace std;
int n, k;
int f[2][2][2];//f[i][j][s] = kkk;//1~i行放了国王,总共放了j个国王,s代表第i行哪些位置有国王,kkk代表方案数量  
int main(){
	cin >> n >> k;//n*n棋盘放k个国王 
	f[0][0][0] = 1;
	//O(n*k*2^(2*n))(最多)
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= k; j++)
			for (int s = 0; s < (1 << n); s++)//从f[i][j][s]状态向外转移,在第i+1行放国王 
				if (f[i][j][s])
					for (int r = 0; r < (1<<n); r++){//怎么放国王 
						if (r&s) continue;//检查有没有国王会被攻击
						if ((r>>1)&s) continue;
						if ((r<<1)&s) continue;
						if ((r<<1)&r) continue;
						f[i+1][j+__builtin_popcount(r)][r] += f[i][j][s];
					}
}

第二种方法:一个一个格子放.
f[i][j][k] = t;//放到了(i,j),放了k个国王,有t种方案

int f[][][][];//f[i][j][k][s]:放到了(i,j)的位置,已经放了k个国王,s代表轮廓线上方每个位置放没放国王
int main(){//O(n^2*k*2^(n+1)),快了一点点(迫真) 
	cin >> n >> k;
	f[1][0][0][0] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
		{
			
			for (int r = 0; r <= k; r++)
				for (int s = 0; s < (1<<(n+1)); s++)
					if (f[i][j-1][r][s]){//可能有这样的结果(比如你不能第一个还没放,国王就已经放完了(i = 0, j = 0, k = 8, s = 0, n = 0) 
						//2种,(i,j)放不放国王 
						//2.不放 
						f[i][j][r][(s|(1<<(j-1)))^(1<<(j-1))] += f[i][j-1][r][s];
						//1.放
						if (j >= 2 && (s>>(j-2)&1) == 1) continue;
						if (j >= 1 && (s>>(j-2)&1) == 1) continue;
						if ((s>>(j+1)) == 1) continue;
						if ((s>>(j+1)&1) == 1) continue
						f[i][j][r][s|(1<<(j-1))] += f[i][j-1][r][s]; 
					}
			
			for (int r = 0; r <= k; r++)
				for (int s = 0; s < (1<<(n+1)); s++)
					f[i+1][0][r][s&((1<<n)-1)<<1] = f[i][j][r][s];
		}
} 

posted on 2025-05-04 11:57  穆昕雨  阅读(10)  评论(0)    收藏  举报

导航