练习专题参考:传送门
HDU 1520
题意:给出n个点,然后给出n个点对应的欢乐值,然后给出n-1条边,a b,表示b是a的直属上级,现在举行一个patry,但是要求员工和他的直属上级不能同时来,问来的人的欢乐值的最大和是多少
思路:首先明确这是一棵有向树,dp[i][0/1]代表第i个人来/不来的最大欢乐值。然后根据状态转移即可
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int INF = 2e9;
const int qq = 6005;
int dp[qq][2];
vector<int> G[qq];
int deg[qq], rat[qq];
int n;
void Dfs(int u) {
for(int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i];
Dfs(v);
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
dp[u][1] += rat[u];
return;
}
int main(){
while(scanf("%d", &n) != EOF) {
REP(i, 1, n) {
scanf("%d", rat + i);
}
int a, b;
while(scanf("%d%d", &a, &b) == 2) {
if(!a && !b) break;
G[b].pb(a), deg[a]++;
}
mst(dp, 0);
for(int i = 1; i <= n; ++i) {
if(!deg[i]) {
Dfs(i);
printf("%d\n", max(dp[i][0], dp[i][1]));
break;
}
}
for(int i = 0; i < qq; ++i) {
G[i].clear();
}
}
return 0;
}
HDU 1561
最开始思路:利用拓扑来解, 但是。。。 WA
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
typedef pair<int, int> pill;
const int qq = 200 + 10;
vector<int> G[qq];
int n, m, val[qq], deg[qq];
bool Cmp(const pill &a, const pill &b) {
return a.first > b.first;
}
int main(){
while(scanf("%d%d", &n, &m) != EOF) {
mst(deg, 0);
REP(i, 0, qq - 1) {
G[i].clear();
}
int x;
REP(i, 1, n) {
scanf("%d%d", &x, val + i);
if(x == 0) continue;
deg[i]++, G[x].pb(i);
}
priority_queue<pill> Q;
for(int i = 1; i <= n; ++i) {
if(deg[i] == 0) {
Q.push(mk(val[i], i));
}
}
int ans = 0;
while(!Q.empty() && m--) {
pill tmp = Q.top();
Q.pop();
ans += tmp.first;
int u = tmp.second;
for(int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i];
deg[v]--;
if(!deg[v]) Q.push(mk(val[v], v));
}
}
printf("%d\n", ans);
}
return 0;
}
可以试试这组数据
7 6 2 8 6 5 0 7 0 8 0 10 7 10 4 4 ans:45
拓扑出来的结果是 44
正确思路:dp[i][j]代表以结点i为根的子树上取包括结点i的j个时的最大价值,如何保证取得长度是链呢 ? 用dp的状态来保证#include <cstdio> #include <cstring> #include <cmath> #include <iostream> #include <algorithm> #include <sstream> #include <map> #include <set> #include <vector> #include <utility> #include <queue> #include <stack> using namespace std; #define LL long long #define pb push_back #define mk make_pair #define pill pair<int, int> #define mst(a, b) memset(a, b, sizeof a) #define REP(i, x, n) for(int i = x; i <= n; ++i) const int MOD = 1e9 + 7; const int qq = 250 + 10; int n, m; int dp[qq][qq], num[qq]; int mp[qq][qq], vis[qq]; void Dfs(int u) { vis[u] = true; for(int i = 1; i <= num[u]; ++i) { int v = mp[u][i]; if(!vis[v]) Dfs(v); for(int j = m; j >= 2; --j) { for(int k = 1; k < j; ++k) { if(dp[u][j - k] != -1 && dp[v][k] != -1) { dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]); } } } } } int main(){ while(scanf("%d%d", &n, &m) != EOF) { if(!n && !m) break; int a, b; dp[0][1] = 0; mst(num, 0); for(int i = 1; i <= n; ++i) { scanf("%d%d", &a, &b); dp[i][1] = b; // 保证状态的更新一定是一条链、 mp[a][++num[a]] = i; } m++; for(int i = 0; i <= n; ++i) { dp[i][0] = 0, vis[i] = 0; for(int j = 2; j <= m; ++j) { //其他暂时不能达到的状态赋值为-1 dp[i][j] = -1; } } Dfs(0); printf("%d\n", dp[0][m]); } return 0; }
codeforces 219D
题意:n个结点,n-1条有向边,如果边是无向边的话那么从任意结点出发可以到达任意结点,现在问要选首都,要求首都能到达其他任意节点,问你最小要求改变几条边的方向,然后还要输出首都的结点编号。
思路:首先以1为根结点进行dfs,求出以u为根结点到达其子树时最小改变几条边的方向,然后再进行一次dfs这次维护以u个首都到达其他所有结点所需要改变边方向数目
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <map>
#include <set>
#include <vector>
#include <utility>
#include <queue>
#include <stack>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 2e5 + 10;
int n, s, t;
vector<int> e[qq];
vector<int> f[qq];
int dp[qq], dir[qq];
void Dfs(int u, int fa) {
dp[u] = 0;
for(int i = 0; i < (int)e[u].size(); ++i) {
int v = e[u][i];
if(v == fa) continue;
dir[v] = f[u][i];
Dfs(v, u);
dp[u] += dp[v] + dir[v];
}
}
void Solve(int u, int fa) {
for(int i = 0; i < (int)e[u].size(); ++i) {
int v = e[u][i];
if(v == fa) continue;
dp[v] = dp[u] + (dir[v] == 0 ? 1 : -1);
Solve(v, u);
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i < n; ++i) {
scanf("%d%d", &s, &t);
e[s].pb(t), f[s].pb(0);
e[t].pb(s), f[t].pb(1);
}
Dfs(1, -1);
Solve(1, -1);
int minx = 1e9;
for(int i = 1; i <= n; ++i) {
minx = min(minx, dp[i]);
}
printf("%d\n", minx);
for(int i = 1; i <= n; ++i) {
if(dp[i] == minx) {
printf("%d ", i);
}
}
puts("");
return 0;
}