题意
有人要邀请一个等级森严的公司里的一些员工参加聚会,但如果邀请了某个员工的上司,那么这个人就不会参加聚会。求能参加聚会的最大人数和邀请方案是否是唯一的。
题解
看到本题第一想法是套最大独立集,由于还要求邀请方案是否唯一,就采用类似刷表的标记。
虽然AC了,但代码感觉很丑。紫书上给出了领一种动态规划的方法。感觉紫书上的方法更好一些。
动态规划做到现在也有点能区分出各种动态规划的区别了,哈
9月25日更新
先贴出基本的最大独立集的状态转移方程和紫书上本题的转移方程
基本的最大独立集:
本题的状态转移方程:
首先,本题的转移方程有一个特点。它的状态是基本最大独立集的状态的细分。可以看到本题的最终答案是在和中取最大值,而这也对应着第一个状态转移方程的前半部分与后半部分。从直观上看,状态简单其转移方程也简单,状态复杂要考虑的情况也就越多,其转移方程就相对复杂。
其次,本题的状态转移方程如此设计有以下两个优点:
1. 本题的状态转移方程只涉及其子节点。通过增加一个状态变量,显式体现了选该点还是不选该点的决策,从而避免了对其孙子的访问。这就为记忆化搜索和递推的实现提供了方便。
2. 这点比较具体,这种状态转移方程方便确定方案是否唯一。在判断方案是否唯一时,如果选该点和不选该点的节点数相同,那么该点为根的子树方案不唯一。这点两种状态转移方程都很好判断。而问题在于,当该点的父节点(或祖父节点)选择该节点时,其方案也是不唯一的。这就要求我们要知道每一步选择了那个决策,并且这个决策是否是一个多方案决策。由于我用了第一种状态转移方程,因此只能另开数组刷是否唯一的状态。若采用紫书上的状态转移方程,则开一个同状态数组跟着u转移即可。
直观来看,这不就是在动态规划中同时维护两个值(类似[线性dp] Mathematical Curse)。同时维护的多个值得转移要保持同步。实现的方式也很多,比如另开数组,维护结构体([01背包] Jin Ge Jin Qu hao UVa12563),增加状态,当然这些本质都相同。
AC代码
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 200 + 10;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
int boss[maxn];
int gs[maxn], s[maxn];
bool flag[maxn][2],f;
int n;
int dp(int u){
if(G[u].empty()){
if(u)s[boss[u]] ++;
if(u && boss[u])gs[boss[boss[u]]]++;
return 1;
}
for(int i = 0; i<G[u].size(); i++){
dp(G[u][i]);
}
int du = max(1+gs[u], s[u]);
if(u)s[boss[u]] += du;
if(u && boss[u]) gs[boss[boss[u]]] += du;
if(s[u] == gs[u]+1){
if(u==0) f = true;
if(u)flag[boss[u]][0] = true;
if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
}
if(du == gs[u]+1 && flag[u][1]){
if(u==0) f = true;
if(u)flag[boss[u]][0] = true;
if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
}
else if(du == s[u] && flag[u][0]){
if(u==0) f = true;
if(u)flag[boss[u]][0] = true;
if(u&&boss[u]) flag[boss[boss[u]]][1] = true;
}
return du;
}
int main(){
ios::sync_with_stdio(false);
while(cin>>n && n){
for(int i = 0; i<=n; i++) G[i].clear();
memset(gs, 0, sizeof(gs));
memset(s, 0, sizeof(s));
memset(flag, false, sizeof(flag));
f = false;
unordered_map<string, int> mp;
string bb,em;
int ncnt = 0;
cin >> bb;
mp[bb] = ncnt++;
int ne, nb;
for(int i = 1; i<n; i++){
cin >> em >> bb;
auto p = mp.find(em);
if(p == mp.end()){
ne = ncnt;
mp[em] = ncnt++;
}
else ne = p->second;
p = mp.find(bb);
if( p == mp.end()){
nb = ncnt;
mp[bb] = ncnt++;
}
else nb = p->second;
G[nb].push_back(ne);
boss[ne] = nb;
}
cout<<dp(0)<<" ";
if(f) cout << "No"<< endl;
else cout << "Yes" << endl;
}
}