2022.7.23测试 —— DP专场
1.AcWing 1077. 皇宫看守
Desctiption
求给定的带权树的符合下面条件的子顶点集合 V:
1. 若点 i∈V,则所有与 i 相邻的点j 可被标号。
2. 任意一个点 j,若 j 不属于 V,则 j 可被标号。
3. S=∑(k[i],i∈V),并且 S 最小。
Solution
考虑树形 DP,发现如果每个只设置是否放守卫两个状态并不可行,考虑更多的状态。
设以 1 号点为根, f[i][0/1/2] 分别表示:
f[i][0] 表示在 i 号节点不放守卫,但能够被其父亲节点放的守卫瞭望到的最小花费;
f[i][1] 表示在 i 号节点不放守卫,但能够被其至少一个子节点放的守卫瞭望到的最小花费;
f[i][2] 表示在 i 号节点放守卫的最小花费。
设 j 为 i 的子节点,考虑转移。
- 若为 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 的矩形,每次可以横或竖按照整数边切一刀,切到所有都是正方形为止,问最少切出多少个正方形。
1≤n,m≤100。
Solution
样例非常凉心地卡掉了你的贪心想法
发现 n,m 都不大,考虑 DP 递推。
设 f[i][j] 表示一个 i×j 的矩形最少切出正方形个数,很明显,当 i=j 时,f[i][j]=1。
我们枚举在哪个位置切下去,最小的正方形个数就是切成两半的个数之和,取最小值即可。
f[i][j]=min(f[i−k1][j]+f[k1][j],f[i][j−k2]+f[i][k2])(1≤k1≤i,1≤k2≤j)
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 的方格矩阵,有些方格是一片水域。你的农村伯伯听说你是学计算机的,给你出了一道题: 他问你:这片农田总共包含了多少个不存在水域的正方形农田。
两个正方形农田不同必须至少包含下面的两个条件中的一条:
-
边长不相等;
-
左上角的方格不是同一方格。
Solution
我们需要考虑如何不重复、不遗漏地统计出所有的正方形农田,根据性质1、2,可以考虑以每个点为右下角的正方形个数数量,答案即为所有点数量总和。
现在考虑如何计算正方形个数,发现个数恰好是以其为右下角最大正方形的边长。
直接转化问题,设 f[i][j] 表示以 (i,j) 为右下角的最大正方形的边长,
由这题启发,则转移方程可以推出:
f[i][j]=min(f[i−1][j−1],f[i−1][j],f[i][j−1])+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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析