传送门ios
思路:算法
咱们须要抓住惟一的重要信息点"ci",个人作法也是在猜测和尝试中得出的,以后再验证算法的正确性。spa
咱们在构造中发现,若是树上出现了相同的数字,则会让树的构造变得不清晰。code
咱们尝试用不一样的数值a[1]~a[n]去构造树,咱们惟一知道的信息就是"ci",若是a[1]~a[n] = 1~n(从小到大排序),则咱们容易肯定root的数值id[root] = a[c[root] + 1]。为何?由于咱们有1~n这n个数字,若是咱们id[root] = a[c[root] + 1],则root下面的点,不管怎么放置这n-1个数字都知足c[root]。若是该root的左边第一个son节点的c[x] = t,则id[x]为第c[x] + 1个数字(由于id[root]被使用了)好像也行的通(红字带入也行得通),而后我按着从左开始的dfs序模拟,发现问题就解决了,这样的方法是行的通的。为何?模拟了以后才发现,由于咱们选的是不一样的数字,咱们若是按着从左的dfs序一个个的解决子树,只要有足够的不一样数字,则该方法必定是能够构造出当前子树的信息(带入红字理解),即子树之间独立不影响。用这个构造方法以前只须要先判断下全部的ci是否是合法,即该点下面若是只有x个数字,c[now]>x就是不合法的。代码下面有个样例,模拟下就理解了。blog
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <queue> #include <string> #include <map> #include <set> using namespace std; const int N = 2e3 + 10; vector<int > E[N]; set<int > hav; int son[N], id[N]; int error; int fun (int now, int pre) { int sn = 0; for(auto to : E[now]) { if(to == pre) continue; sn += fun(to, now); } if(son[now] - 1 > sn) error = 1; ///判断ci是否是合法 return sn + 1; } void dfs (int now, int pre) { if(son[now] > hav.size()) { error = 1; return; } else { int tot = 0; for(auto& x: hav) { ++tot; if(tot == son[now]) { id[now] = x; // cout << x << endl; hav.erase(x); break; } } } for(auto to : E[now]) { if(to == pre) continue; dfs(to, now); if(error) return; } } void solve() { int n, root; scanf("%d", &n); for(int i = 1; i <= n; ++i) { int x, cnt; scanf("%d%d", &x, &cnt); if(x != 0) { E[i].push_back(x); E[x].push_back(i); } else root = i; son[i] = cnt + 1;///第几个数字 } for(int i = 1; i <= n; ++i) { hav.insert(i); } error = 0; fun(root, 0); if(error) { printf("NO\n"); return; } dfs(root, 0); if(error) { printf("NO\n"); } else { printf("YES\n"); for(int i = 1; i <= n; ++i) { printf("%d ", id[i]); } printf("\n"); } } int main() { solve(); return 0; } /* 13 0 5 1 2 2 0 2 1 4 0 4 1 6 0 6 0 1 1 9 0 9 2 11 0 11 0 */