DP选做

DP选做

P4805 [CCC2016] 合并饭团 [区间DP] [单调性优化] [提高+/省选-]

和普通区间DP有一点点区别,就是多加了一个三个区间的合并

至于普通合并是\(n^4\),再加一个单调性优化即可变成\(n^3\)

即左区间的右端点右移时右区间的左端点一定左移,区间相等后还要看中间区间能不能变成一个饭团

P2238 逛庙会 [普通DP] [状态压缩] [省选/NOI-]

考虑一开始想的是压上下左右四个状态,后面发现只有下和右状态有用

接下来发现做法假了,比如说从上往下转移时,转移到的格子的左边那个格子的状态不见了

后来发现需要记录4个格子

然后写了\(\frac 1 3\)个上午,细节有点多,把代码贴出来吧

check: (int i,int j,int k){
	if(get(k,0) && (i == 0 || shop[i-1][j+1] == 0)) return 1;
    if(get(k,1) && shop[i][j+1] == 0)return 1;
   	if(get(k,2) && shop[i+1][j] == 0)return 1;
   	if(get(k,3) && (j == 0 || shop[i+1][j-1] == 0)) return 1;
	return 0; 
}

main:
	for(int k = 0; k <= 15; ++k)dp[1][1][k] = 0;
    for(int i = 1; i <= H; ++i){
    	for(int j = 1; j <= W; ++j){
    		if(i == 1 && j == 1)continue;
    		for(int k = 0; k < 16; ++k){
    			if(get(k,1) && get(k,2))continue;//下和右不可能都没与处理
    			if(check(i,j,k)) continue;
    			int up,down,left,right,now; 
    			balabala...//上向下 
				for(int l = 0; l < 16; ++l){
    				if(get(k,0) == 0 && shop[i-1][j+1] != 0)break;//0号点一定没处理过 
    				if(check(i,j-1,l))continue;//检查合法性 
    				if(get(l,2) != get(k,3)) continue;//前面的二号点等于现在的三号点 (继承)
    				if(get(k,1) + get(k,2) > 1)continue;//没处理的点的个数不能超过1个 
    				left = 0,down = (get(k,2)^1) * shop[i+1][j],right = (get(k,1) ^ 1) * shop[i][j+1];
    				if((get(k,1) || get(k,2)) && get(l,0)) up = shop[i-1][j];//上面的点如果能不处理就不处理 
    				else up = 0;
    				now = get(l,1) * shop[i][j];//左面没处理当前点就处理 
    				dp[i][j][k] = min(dp[i][j-1][l] + up + down + left + right + now,dp[i][j][k]);
				}//左向右  
			}
		}
	}
P2051 [AHOI2009] 中国象棋 [普通DP] [组合数学] [打表] [提高+/省选-]

法一:DP

状态设置:

一开始想到的是部分分十分的显然,就是一个状压DP,但是有没有一种可能,对于前\(~i~\)行这道题只需要知道几列没放,几列放了一个,几列放了两个

\(~dp[i][j][k]~\)表示对于前\(~i~\)行,有\(~j~\)列放了一个,有\(~k~\)列放了两个

\[dp[i][j][k] = max\begin{cases}dp[i-1][j][k]&该行不放\\dp[i-1][j-1][k]\times \binom 1 {m - j - k + 1}&放一个到没有棋子的一列\\dp[i-1][j+1][k-1]\times \binom 1 {j+1}&放一个到有一个棋子的一列\\dp[i-1][j-2][k]\times \binom 2 {m-j-k+2}&放两个到没有棋子的两列\\dp[i-1][j][k-1]\times \binom 1 j \times \binom 1 {m-j-k+1}&放两个到一列有一个棋子、一列没有棋子上\\dp[i-1][j+2][k-2]\times\binom 2 {j+2}&放两个到有一个棋子的两列\end{cases} \]

法二:打表

考虑把表打完好像交不了,那就打像三角形一样的一半即可,这里就不给出打的表了,有点长……

P3177 [HAOI2015] 树上染色 [树型DP] [上下界优化] [提高+/省选-]

计算每一条边的贡献,就是(下面的黑点数×上面的黑点数+下面的白点数×上面的白点数) ×边的边权

考虑需要进行上下界优化

void dfs(int u,int fa){
	siz[u] = 1; 
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to,val = edge[i].val;
		if(v == fa) continue;
		dfs(v,u);
		siz[u] += siz[v];
		for(int j = min(siz[u],m); j >= 0; --j)
			for(int k = max(0,j - (siz[u] - siz[v])); k <= min(siz[v],j); ++k){
				if(dp[u][j-k] == -1) continue;
				ll tot = val * ((ll) k * (m - k) + ((ll)siz[v] - k) * (n - m - siz[v] + k));
				dp[u][j] = max(dp[u][j],dp[u][j - k] + dp[v][k] + tot);
			}
	}
}

本质上就是说对\(~j~\)的枚举进行一个剪枝

[POI2017]Sabotaż [树状DP] [提高+/省选-]

solution 1:二分答案

考虑每次二分一个答案,然后进去去跑一遍看他能不能跑出\(~k~\)个间谍,理论上\(~O(nlog_n)~\)不知道能不能过

solution 2:树状DP

考虑设计状态,令\(~dp[i]~\)表示让\(~i~\)以及它的子树不全部叛变的最小的\(~x~\),即全部叛变的最大的\(~x~\),定义\(~siz[i]~\)为以\(~i~\)为子树的点的个数

\[dp[u] = max(dp[u],min(dp[v],\frac {siz[v]}{siz[u]-1})) \]

\[ans = max(dp[x]),x\in [1,n],k\leq siz[x],x为整数 \]

特别地,叶子节点的\(~dp~\)值为\(~1.00~\)

解释:

  • 一个子树让它的父亲叛变的条件需要都满足(子树叛变,并且\(~x~\)要小于该子树所占的比例),所以取\(~min~\)
  • 全部叛变最大的\(~x~\),所以取\(~max~\)
  • \(~ans~\)\(~max~\)同理
P3195 [HNOI2008] 玩具装箱 [普通DP] [斜率优化] [省选/NOI-]

斜率优化板子

P2120 [ZJOI2007] 仓库建设 [普通DP] [斜率优化] [省选/NOI-]

斜率优化,然后有一些点需要注意

  1. 在末尾可能有\(~p_i=0~\)的无效工厂,这些工厂不需要在\(~n~\)点额外建造一个仓库;
  2. 有可能全部工厂都是无效工厂,注意特判边界;
  3. 斜率优化有条件的请使用叉积,如果用除法请注意分母为\(~0~\)的情况;
  4. long double 能够帮助你解决一部分掉精度的问题,当然本题不一定需要;
P2490 [SDOI2011]黑白棋 [博弈论] [计数DP] [组合数] [省选/NOI-]

K-nim游戏 即给你\(~n~\)堆石子,你一次可以取最多\(~K~\)堆,最后取的人获胜

我们将所有数进行二进制拆分,每一位上的和为\(r_i\),如果\(\exist i\in \Z,~r_i\mod k+1~ \not = 0\)那么先手必胜;否则先手必败

prove:

如果所有的\(~r_i~\)都为\(~0~\),则一次操作肯定会使得某些数位上的\(~r~\)值改变。又因为一堆石子每位上都是\(~0~\)\(~1~\),所以变动的绝对值不会超过\(~k~\)。因此操作结束后该位置上的\(~r_i\mod {(k+1)}~\)必不能为\(~0~\)
否则,另一名玩家一定可以通过一次操作将所有的\(~r_i~\)变成\(~0~\)

这道题,不一样的是,最后取的为失败,那么我们就设\(~dp[i][j]~\)表示前\(~1\to n~\)位的\(~r~\)都是\(~0~\),现在剩了\(~j~\)个石子的方案数

然后该怎么推怎么推啦!(不想写了逃)

P2605 [ZJOI2010]基站选址 [普通DP] [线段树优化] [NOI/NOI+/CTSC]
P6563 [SBCOI2020] 一直在你身旁 [区间DP] [单调队列优化] [省选/NOI-]

如果我有人一直在我身旁就好了(酸

考虑一眼区间DP

设计状态\(~dp[i][j]~\)表示将电线的长度范围缩小到区间\(~[i,j]~\)的最小代价

那么我们可以知道\(~O(n^3)~\)做法:

\(dp[i][j] = min(dp[i][j],max(dp[i][k],dp[k+1][j])+a[k])\)

看数据范围,好像是\(O(n^2)\)

这个区间的枚举显然是不怎么变的,如果要变的话也只能是把\(~k~\)的转移进行一个优化

如果说把\(~max()~\)​这个可恶的东西去掉,那么就显然是一个单调队列优化

如果只是从\(~dp[i][k]+a[k]~\)转移的话,那么就是单调递增的,取最左边的即可

如果只是从\(~dp[k+1][j]+a[k]~\)转移的话,那么就是一个单调队列即可

加上\(~max()~\)就是进行一个分类讨论

那么当\(~dp[i][k]>dp[k+1][r]~\)时从\(~dp[i][k]+a[k]~\)转移,随着\(~r~\)指针右移,\(~k~\)指针左移

那么当\(~dp[i][k]<dp[k+1][r]~\)时从\(~dp[i+1][j]+a[k]~\)转移,随着\(~r~\)指针右移,向单调队列中往右边加入值,单调队列从左至右单减

这样我们的循环也要变一下,先\(~r~\)\(~l~\)

P1131 [ZJOI2007] 时态同步 [数型DP?] [贪心] [提高+/省选-]

考虑不像DP,更像贪心

你知道,如果说在不改变到根节点距离最长的子节点的长度的情况下,改变的边的长度越深,方案越优

then,就贪心完了呗

void dfs(int u,int fa){
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		dfs(v,u);
		dis[u] = max(dis[u],dis[v] + edge[i].val);
	}
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		ans += dis[u] - dis[v] - edge[i].val;
	}
}

\(~dis[u]~\)表示\(~u~\)的子树中,最长的子树中节点到\(~u~\)的距离

P2466 [SDOI2008] Sue 的小球 [区间DP] [预处理当前选择对将来选择的影响] [省选/NOI-]

这道题,一看就是一个区间DP,然后呢……(LXW停止运行,正在向HF报告错误

考虑,你显然要先排一遍序

然后可以设计状态:

\[dp[i][j][k]表示,收集了第~i \sim j~的个球的得分的最大值,k = 0时在~i~,k = 1时在~j~ \]

考虑得分这个东西好像不太好搞,那么我们可以把最大得分改为最小失分

then,你可以发现一些奇妙的发现:

你并不能确定\(~dp[i][j][k]~\)的时间是多少,那么你的失去的分数就不太好算,这时候就到了这到题的精华

预前处理当前选择对将来选择的影响!

比如说,你从\(~dp[i+1][j][0]~\)转移到\(~dp[i][j][0]~\)

  • 在你移动的这段时间里,\(~1\sim i~\)\(~j+1 \sim n~\)的所有彩蛋都会减少一些分数,这些分数即是转移时需要加上的值

最后是一段核心转移code

为了写起来方便,把\(~f[i][j][0]~\)变成了\(~f[i][j]~\),把\(~f[i][j][1]~\)变成了\(~g[i][j]~\)

\(~pre[i]~\)\(~1\sim i~\)\(~v~\)的和

望知晓!!

f[i][j] = min(f[i][j],f[i+1][j] + (pre[i] - pre[0] + pre[n] - pre[j]) * (ball[i+1].x - ball[i].x));
f[i][j] = min(f[i][j],g[i+1][j] + (pre[i] - pre[0] + pre[n] - pre[j]) * (ball[j].x - ball[i].x));
g[i][j] = min(g[i][j],g[i][j-1] + (pre[i-1] - pre[0] + pre[n] - pre[j-1]) * (ball[j].x - ball[j-1].x));
g[i][j] = min(g[i][j],f[i][j-1] + (pre[i-1] - pre[0] + pre[n] - pre[j-1]) * (ball[j].x - ball[i].x));
P2607 [ZJOI2008] 骑士 [树型DP] [基环树] [省选/NOI-]

就是上司的舞会(树型DP),然后多了你要有很多判环,反正是一道基环树DP板子,看看题解即可。

posted @ 2023-03-20 17:27  ricky_lin  阅读(26)  评论(0编辑  收藏  举报