昨天将一些没有用的可以卖的vps卖了。
今天将原来放在旧主机上的一个服务迁移到了现在的服务器上面。
racknerd上面的服务器我估计不好出,就到时候不续费就好。
现在相当于将每年的服务器成本降低了,太好了。
洛谷 P1272 重建道路
url: https://www.luogu.com.cn/problem/P1272
tag:
动态规划,树形DP
思路:
简单的树形DP和背包结合。令 f[u][j]
表示以u节点为根的子树在需要取出j个节点时最少需要断的边。最后在输出答案的时候在所有节点中选择一个答案最小的,注意选择其他节点为根节点时需要断去父节点和其之间的一条边。最后输出答案即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200;
int h[N], e[N * 2], ne[N * 2], idx;
int s[N];
int n, p;
int f[N][N];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void dfs(int u, int fa)
{
s[u] = 1;
f[u][1] = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (v == fa) continue;
dfs(v, u);
s[u] += s[v];
for (int j = min(s[u], p); j >= 0; j --)
{
f[u][j] += 1;
for (int k = 0; k <= min(j, s[v]); k++)
f[u][j] = min(f[u][j], f[u][j - k] + f[v][k]);
}
}
}
int main()
{
memset(h, -1, sizeof h);
memset(f, 0x3f, sizeof f);
cin >> n >> p;
for (int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs(1, 0);
int res = f[1][p];
for (int i = 2; i <= n; i ++) res = min(res, f[i][p] + 1);
cout << res << endl;
return 0;
}
洛谷 P3177 HAOI2015 树上染色
url: https://www.luogu.com.cn/problem/P3177
tag:
动态规划,树形DP
思路:
用 f[u][j]
表示以u为根节点的子树中,恰好有j个黑点时的收益。之后就是和分组背包类似的思路,对于每一个相连的节点,由当那个节点取k个黑点时更新过来,在不同的k值之间取一个最大值。使用 (k * (m - k) + (s[v] - k) * (n - m - s[v] + k)) * w[i]
来计算u和v之间边对答案的贡献。最后记得开long long。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2020;
int h[N], e[N * 2], ne[N * 2], w[N * 2], idx;
long long n, m;
long long s[N];
long long f[N][N];
void add(int a, int b, int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
void dfs(int u, int fa)
{
s[u] = 1;f[u][0] = f[u][1] = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (v == fa) continue;
dfs(v, u);
s[u] += s[v];
for (int j = min(s[u], m); j >= 0; j --)
for (int k = 0; k <= min((long long)j, s[v]); k ++)
if (f[u][j - k] != -1)
{
long long val = (k * (m - k) + (s[v] - k) * (n - m - s[v] + k)) * w[i];
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k] + val);
}
}
}
int main()
{
memset(h, -1, sizeof h);
memset(f, -1, sizeof f);
cin >> n >> m;
for (int i = 0; i < n - 1; i ++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs(1, 0);
cout << f[1][m] << endl;
return 0;
}
洛谷 P4170 CQOI2007 涂色
url: https://www.luogu.com.cn/problem/P4170
tag:
字符串,动态规划,枚举,区间DP
思路:
使用 f[i][j]
表示从i到j这个区间中如果要涂到规定的情况,最少需要的涂色次数。因为有区间,所以可以使用区间DP,对于每一各区间i到j来说如果第i个字符和第j个字符相同,则 f[i][j]
可以从 f[i][j - 1]
更新过来。如果不相同,则可以枚举这个区间中的每一个位置,将这个区间变为两个区间分别进行,由两个区间进行更新。初始状态,每个长度为1的区间,要达到规定的情况只需要涂一次就行。最后输出 f[0][n - 1]
即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 60;
int f[N][N];
int main()
{
char s[60];
cin >> s;
int n = strlen(s);
for (int i = 0; i < n; i ++) f[i][i] = 1;
for (int l = 1; l < n; l ++)
{
for (int i = 0; i + l < n; i++)
{
if (s[i] == s[i + l]) f[i][i + l] = f[i][i + l - 1];
else
{
f[i][i + l] = f[i][i] + f[i + 1][i + l];
for (int k = i + 1; k < i + l; k ++)
f[i][i + l] = min(f[i][i + l], f[i][k] + f[k + 1][i + l]);
}
}
}
cout << f[0][n - 1] << endl;
return 0;
}
UVA1629 切蛋糕 Cake slicing
url: https://www.luogu.com.cn/problem/UVA1629
tag:
动态规划,枚举,前缀和
思路:
用 dp[lx][ly][rx][ry]
来记录某一个区间中,为使每一块蛋糕都有樱桃的最小代价。使用dfs来枚举每一种可能性。使用记忆化搜索来减少时间。前缀和快速计算出某一块区域樱桃的数量。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
int dp[N][N][N][N];
int p[N][N];
int cs;
int pnum(int lx, int ly, int rx, int ry)
{
return p[rx][ry] - p[lx - 1][ry] - p[rx][ly - 1] + p[lx - 1][ly - 1];
}
int DP(int lx, int ly, int rx, int ry)
{
if (pnum(lx, ly, rx, ry) == 0) return 0x3f3f3f3f;
if (pnum(lx, ly, rx, ry) == 1) return 0;
int &d = dp[lx][ly][rx][ry];
if (d != 0x3f3f3f3f) return d;
for (int i = lx; i < rx; i ++)
d = min(d, DP(lx, ly, i, ry) + DP(i + 1, ly, rx, ry) + ry - ly + 1);
for (int i = ly; i < ry; i ++)
d = min(d, DP(lx, ly, rx, i) + DP(lx, i + 1, rx, ry) + rx - lx + 1);
return d;
}
int main()
{
int n, m, k;
while(scanf("%d%d%d", &n, &m, &k) != EOF && n && m)
{
memset(p, 0, sizeof p);
for (int i = 0; i < k; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
p[a][b] = 1;
}
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
p[i][j] += p[i - 1][j] + p[i][j - 1] - p[i - 1][j - 1];
memset(dp, 0x3f, sizeof dp);
printf("Case %d: %d\n", ++cs, DP(1, 1, n, m));
}
return 0;
}