问题概述
原题参考:G. Vlad and Trouble at MIT
某学校的宿舍可以用一棵n个顶点的树来表示,每个顶点代表一个房间,房间内有一个学生,树是一个联通的无向图。今天晚上有三种学生:
- 参加派对和玩音乐的学生(标记为P)
- 想睡觉和享受安静的学生(标记为S)
- 无所谓的学生(标记为C)
起初,所有的边缘都是薄墙,允许音乐通过,因此当参加派对的学生放音乐时,每个房间都能听到,但是我们可以将薄墙改装成厚墙,厚墙不允许音乐通过。问若要是所有的同学都满意,至少要改装多少面墙
思路分析
其实当时是看出来这是一道树上DP的问题的,但是奈何自己不会写(作孽啊)。dp[i][0]表示i号结点为S时作为子树的最小的墙,dp[i][1]表示i号结点为P时作为子树的最小的墙。可以知道,当i号结点为S时,dp[i][1]=inf;同理,当i号结点为P时,dp[i][0]=inf,作为C的话,就是无所谓了,dp[i][0]=dp[i][1]=0。对于一个点,只有当子树的状态与自己不同时才会需要建墙,当子树的状态与自己相同时,是不需要建墙的,因此,得出状态转移方程
dp[u][k] = min(dp[v][k^0]+1, dp[v][k])(v为u的子结点,k=0/1)
参考代码
#include <bits/stdc++.h>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
#define pll pair<long long, long long>
#define pii pair<int, int>
#define vi vector<int>
#define vl vector<long long>
#define ll long long
#define ull unsigned long long
const ll INF = 9187201950435737471;
const int inf = 2139062143;
const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int N = 2e5+7;
ll n, dp[N][2];
vi e[N];
string s;
void dfs(int u) {
dp[u][0] = dp[u][1] = 0;
if(s[u-1] == 'P') dp[u][0] = inf;
if(s[u-1] == 'S') dp[u][1] = inf;
for(auto v:e[u]) {
dfs(v);
dp[u][0] += min(dp[v][0], dp[v][1]+1);
dp[u][1] += min(dp[v][0]+1, dp[v][1]);
}
}
void solve() {
cin >> n;
int x;
for(int i=2; i<=n; i++) {
cin >> x;
e[x].push_back(i);
}
cin >> s;
dfs(1);
cout << min(dp[1][0], dp[1][1]) << endl;
for(int i=1; i<=n; i++) e[i].clear();
}
int main() {
#ifdef xrl
freopen("in.txt", "r", stdin), freopen("out.txt", "w", stdout);
#endif
FAST_IO;
int t = 1;
cin >> t;
while(t --) solve();
#ifdef xrl
cout << "Time used = " << (double)(clock() * 1.0 / CLOCKS_PER_SEC) << "s";
#endif
return 0;
}
做题反思
关键在于状态转移方程的思考啊啊啊啊啊,dp杀我
浙公网安备 33010602011771号