2022.7.23测试 —— DP专场
1.AcWing 1077. 皇宫看守
Desctiption
求给定的带权树的符合下面条件的子顶点集合 :
1. 若点 ,则所有与 相邻的点 可被标号。
2. 任意一个点 ,若 不属于 ,则 可被标号。
3. ,并且 最小。
Solution
考虑树形 DP,发现如果每个只设置是否放守卫两个状态并不可行,考虑更多的状态。
设以 号点为根, 分别表示:
表示在 号节点不放守卫,但能够被其父亲节点放的守卫瞭望到的最小花费;
表示在 号节点不放守卫,但能够被其至少一个子节点放的守卫瞭望到的最小花费;
表示在 号节点放守卫的最小花费。
设 为 的子节点,考虑转移。
- 若为 情况,说明其父亲节点已经可以管到此点,由于是在回溯中转移,该点的子节点放不放守卫都行,只是因为 不放守卫,不能从 转移。
- 若为 情况,说明这个点放了守卫,则子节点任意状态均可转移。
- 若为 情况,说明这个点能被它的至少一个子节点管到,我们可以枚举它被哪个子节点 管到最划算,那个子节点必须从 转移来,其他子节点的情况除不能从 转移来外任意。
最后答案为 。
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
给定一个 的矩形,每次可以横或竖按照整数边切一刀,切到所有都是正方形为止,问最少切出多少个正方形。
。
Solution
样例非常凉心地卡掉了你的贪心想法
发现 , 都不大,考虑 DP 递推。
设 表示一个 的矩形最少切出正方形个数,很明显,当 时,。
我们枚举在哪个位置切下去,最小的正方形个数就是切成两半的个数之和,取最小值即可。
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
你家有一片 农田,将其看成一个 的方格矩阵,有些方格是一片水域。你的农村伯伯听说你是学计算机的,给你出了一道题: 他问你:这片农田总共包含了多少个不存在水域的正方形农田。
两个正方形农田不同必须至少包含下面的两个条件中的一条:
-
边长不相等;
-
左上角的方格不是同一方格。
Solution
我们需要考虑如何不重复、不遗漏地统计出所有的正方形农田,根据性质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 = 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 IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!