Processing math: 100%

2022.7.23测试 —— DP专场

1.AcWing 1077. 皇宫看守

Desctiption

求给定的带权树的符合下面条件的子顶点集合 V

1. 若点 iV,则所有与 i 相邻的点j 可被标号。

2. 任意一个点 j,若 j 不属于 V,则 j 可被标号。

3. S=k[i]iV,并且 S 最小。

Solution

考虑树形 DP,发现如果每个只设置是否放守卫两个状态并不可行,考虑更多的状态。

设以 1 号点为根, f[i][0/1/2] 分别表示:

f[i][0] 表示在 i 号节点不放守卫,但能够被其父亲节点放的守卫瞭望到的最小花费;

f[i][1] 表示在 i 号节点不放守卫,但能够被其至少一个子节点放的守卫瞭望到的最小花费;

f[i][2] 表示在 i 号节点放守卫的最小花费。

ji 的子节点,考虑转移。

  • 若为 f[i][0] 情况,说明其父亲节点已经可以管到此点,由于是在回溯中转移,该点的子节点放不放守卫都行,只是因为 i 不放守卫,不能从 f[j][0] 转移。

f[i][0]=min(f[j][1],f[j][2])

  • 若为 f[i][2] 情况,说明这个点放了守卫,则子节点任意状态均可转移。

f[i][2]=k[i]+min(f[j][0],f[j][1],f[j][2])

  • 若为 f[i][1] 情况,说明这个点能被它的至少一个子节点管到,我们可以枚举它被哪个子节点 j 管到最划算,那个子节点必须从 f[j][2] 转移来,其他子节点的情况除不能从 f[j][0] 转移来外任意。

f[i][1]=min(f[j][1],f[j][2])min(f[j][1],f[j][2])+f[j][2])

最后答案为 min(f[1][1],f[1][2])

Code

// by youyou2007 in 2022.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#define REP(i, x, y) for(int i = x; i < y; i++)
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define PER(i, x, y) for(int i = x; i > y; i--)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
const int N = 1505;
int n;
vector <int> g[N];
int w[N]; 
int b[N];
int f[N][3];
void dfs(int x, int fa)
{
	f[x][2] = w[x];//注意 f[x][2] 转移前要加上该点的值
	int sum = 0;
	for(int i = 0; i < g[x].size(); i++)
	{
		int xx = g[x][i];
		if(xx != fa)
		{
			dfs(xx, x);
			f[x][0] += min(f[xx][1], f[xx][2]);
			f[x][2] += min(f[xx][0], min(f[xx][1], f[xx][2]));
			sum += min(f[xx][1], f[xx][2]);
		}
	}
	int minn = 0x3f3f3f3f;//下面都是处理 f[x][1] 的情况
	for(int i = 0; i < g[x].size(); i++)
	{
		int xx = g[x][i];
		if(xx != fa)
		{ 
			minn = min(minn, sum - min(f[xx][1], f[xx][2]) + f[xx][2]); 
		}
	}
	f[x][1] = minn;
}
int main()
{
	scanf("%d", &n);
	rep(i, 1, n)
	{
		int id, T, y;
		scanf("%d", &id);
		scanf("%d", &w[id]);
		scanf("%d", &T);
		while(T--)
		{
			scanf("%d", &y);
			g[id].push_back(y);
			g[y].push_back(id);
		}
	}
	dfs(1, 0);
	printf("%d", min(f[1][1], f[1][2]));
	return 0;
}

2. 矩形切割

Description

给定一个 n×m 的矩形,每次可以横或竖按照整数边切一刀,切到所有都是正方形为止,问最少切出多少个正方形。

1n,m100

Solution

样例非常凉心地卡掉了你的贪心想法

发现 nm 都不大,考虑 DP 递推。

f[i][j] 表示一个 i×j 的矩形最少切出正方形个数,很明显,当 i=j 时,f[i][j]=1

我们枚举在哪个位置切下去,最小的正方形个数就是切成两半的个数之和,取最小值即可。

f[i][j]=min(f[ik1][j]+f[k1][j],f[i][jk2]+f[i][k2])(1k1i,1k2j)

Code

// by youyou2007 in 2022.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#define REP(i, x, y) for(int i = x; i < y; i++)
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define PER(i, x, y) for(int i = x; i > y; i--)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
const int N = 105;
int f[N][N];
int n, m;
/*
考虑 f[i][j] 表示长 i,宽 j 的矩形最少砍成几个正方形
*/
int main()
{
	scanf("%d%d", &n, &m);
	memset(f, 0x3f, sizeof f);
	rep(i, 0, min(n, m))
		f[i][i] = 1;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			if(i == j) continue;
			for (int k = 1; k <= i - 1; k++)
			{
				f[i][j] = min(f[i][j], f[i - k][j] + f[k][j]);
			}
			for(int k = 1; k <= j - 1; k++)
			{
				f[i][j] = min(f[i][j], f[i][j - k] + f[i][k]);
			}
		}
	}
	printf("%d", f[n][m]);
	return 0;
}

3.count

Description

你家有一片 N×M 农田,将其看成一个 N×M 的方格矩阵,有些方格是一片水域。你的农村伯伯听说你是学计算机的,给你出了一道题: 他问你:这片农田总共包含了多少个不存在水域的正方形农田。

两个正方形农田不同必须至少包含下面的两个条件中的一条:

  1. 边长不相等;

  2. 左上角的方格不是同一方格。

Solution

我们需要考虑如何不重复、不遗漏地统计出所有的正方形农田,根据性质1、2,可以考虑以每个点为右下角的正方形个数数量,答案即为所有点数量总和。

现在考虑如何计算正方形个数,发现个数恰好是以其为右下角最大正方形的边长。

直接转化问题,设 f[i][j] 表示以 (i,j) 为右下角的最大正方形的边长,

由这题启发,则转移方程可以推出:

f[i][j]=min(f[i1][j1],f[i1][j],f[i][j1])+1

然后最后求出总和即可。

Code

// by youyou2007 in 2022.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#define REP(i, x, y) for(int i = x; i < y; i++)
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define PER(i, x, y) for(int i = x; i > y; i--)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
const int N = 1005;
int n, m;
int mapp[N][N], f[N][N];
int ans;
int main()
{
	scanf("%d%d", &n, &m);
	rep(i, 1, n)
	{
		string s;
		cin >> s;
		rep(j, 0, m - 1)
		{
			mapp[i][j + 1] = s[j] - '0';
		}
	}
	rep(i, 1, n)
	{
		rep(j, 1, m)
		{
			if(mapp[i][j] == 1)
			{
				f[i][j] = min(f[i - 1][j - 1], min(f[i][j - 1], f[i - 1][j])) + 1;
				ans += f[i][j];		
			}
		}
	}
	printf("%d", ans);
	return 0;
}
posted @   panjx  阅读(52)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析
点击右上角即可分享
微信分享提示