• abc 319 f ( 状压dp + 树上bfs


    1. #include
    2. using namespace std;
    3. //max min vecotr
    4. //cin cout
    5. using VI = vector<int>;
    6. using PII = pair<int, int>;
    7. using ll = long long;
    8. int idx = 0;
    9. int head[2010];
    10. VI gar[510];
    11. int p[510],t[510],s[510],g[510];
    12. int med[510];//记录每个药水 对应的位置
    13. int pos[510];//记录每个位置 对应的药水编号
    14. ll dp[1<<11];
    15. int fa[510];
    16. int vis[1<<11][510];
    17. int n;
    18. int maxn = 0;
    19. int tot = 0;
    20. priority_queue , greater> pq[1 << 11];
    21. void solve(){
    22. for(int i = 0 ; i < tot ; i++){
    23. queue<int> q;
    24. q.push(med[i]);
    25. while(q.size()){
    26. int u = q.front();
    27. q.pop();
    28. if(pos[u] != -1 && i != pos[u]) fa[pos[u]] = i;
    29. //当前的这个点是一瓶药水,并且不是初始的药水 ,那么就记录这瓶药的最近祖先捏
    30. else for(auto x : gar[u]) q.push(x);
    31. }
    32. }
    33. }
    34. int main(){
    35. memset(pos , -1 , sizeof pos);
    36. memset(med , -1 , sizeof med);
    37. memset(fa , -1 , sizeof fa);
    38. memset(dp , -1 , sizeof dp);
    39. cin>>n;
    40. for(int i = 2; i <= n; i++){
    41. cin>>p[i]>>t[i]>>s[i]>>g[i];
    42. if(t[i] == 2){
    43. med[tot] = i;
    44. pos[i] = tot;
    45. tot++;
    46. }else{
    47. maxn = max(maxn , s[i]);
    48. }
    49. gar[p[i]].push_back(i);
    50. }
    51. ll now = 1 ;
    52. dp[0] = 1;
    53. vis[0][1] = 1;
    54. for(auto to : gar[1]){
    55. if(t[to] == 1) pq[0].push({s[to] , to});
    56. }
    57. while(pq[0].size()){
    58. auto x = pq[0].top().second;
    59. if(now < s[x]) break;
    60. now += g[x];
    61. vis[0][x] = 1;
    62. pq[0].pop();
    63. for(auto to : gar[x]){
    64. if(t[to] == 1) pq[0].push({s[to] , to});
    65. }
    66. }
    67. //11111
    68. dp[0] = now;
    69. solve();
    70. //cout<
    71. /* for(int i = 0 ; i < tot ; i++){
    72. cout<
    73. }
    74. cout<
    75. for(int i = 0 ; i < (1 << tot) ; i++){
    76. if(dp[i] > maxn){
    77. cout<<"Yes\n";
    78. return 0;
    79. }
    80. if(dp[i] < 0) continue;
    81. //cout<
    82. //dp[i] = -1e9;
    83. for(int j = 0 ; j < tot ; j++){
    84. if(!vis[i][p[med[j]]]) continue;
    85. if((i >> j) & 1) continue;
    86. if(fa[j] != -1 && ((i >> fa[j]) & 1) == 0) continue;
    87. int ne = i | (1 << j);
    88. ll val = dp[i] * g[med[j]];
    89. // priority_queue , greater> z;
    90. auto z = pq[i];
    91. for(int k = 1 ; k <= n ; k++) vis[ne][k] = vis[i][k];
    92. vis[ne][med[j]] = 1;
    93. for(auto u : gar[med[j]]){
    94. if(t[u] == 1) z.push({s[u] , u});
    95. }
    96. while(z.size()){
    97. auto x = z.top().second;
    98. if(val < s[x]) break;
    99. val += g[x];
    100. vis[ne][x] = 1;
    101. z.pop();
    102. for(auto to : gar[x]){
    103. if(t[to] == 1) z.push({s[to] , to});
    104. }
    105. }
    106. if(val > dp[ne]) dp[ne] = val , pq[ne] = z;
    107. }
    108. }
    109. cout<<"No\n";
    110. }

    只能说这个树上状压属实给我干崩溃

    首先一定是尽量先打怪再吃药,于是就可以通过枚举药水的喝法 , 解决问题

    1.如果当前状态为 i   想喝第 j 瓶药水,那么这个药水j的 父节点是必须的走到的

    2.当前这个的如果存在一个祖先为药水,那么祖先必定的被喝,

    3.当前药水  j 不在 状态 i 中

    然后考虑每次该怎么操作,对于状态 i - > ne

    对于每个状态都开一个堆,堆里剩下的都是杀不掉的怪

    dp的值一定是变大的,所以我们新建一个堆,继承 i 里面的所有值(状态 i 杀不掉的)

    同时将  j 药水 可达的怪兽点也加入到堆中,

    然后就开始从小到大,开始刷怪,遇到刷不动的怪就结束。

    ——————————————————————

    建议下次设变量的时候把 id 和 pos 换个位置

    id 记录当前位置的编号,pos 记录当前编号的位置

  • 相关阅读:
    【云原生之k8s】k8s资源限制以及探针检查
    springboot笔记总结
    Redis的数据结构分析
    TLS 的证书验证过程
    Ribbon负载均衡服务调用
    数据结构——图结构
    Qt-OpenCV学习笔记--边缘检测--Canny()
    【计算机视觉】图像分割与特征提取——基于Log、Canny的边缘检测
    MyBatisPlus 入门教程,这篇很赞
    在移动固态硬盘上安装Ubuntu系统和ROS2
  • 原文地址:https://blog.csdn.net/m0_75125820/article/details/132885530