zoukankan      html  css  js  c++  java
  • [ZJOI2008]骑士 DP dfs

    ~~~题解~~~

    题解:

      观察题面可以很快发现这是一棵基环内向树(然而并没有什么用。。。)

      再稍微思考一下,假设将这个环中的任意一点设为root,然后去掉root到下面的特殊边(即构成环的那条边),那么就构成了一棵树,并且可以用简单树形DP解决。

      再考虑加上这条边的限制,设被去掉的这条边是连接root 和 x的, 这条边实际上就是限制了在选root的时候不能选x,那么考虑一个暴力的想法。

      我们先在图中dfs,找到这个环,然后任意指定一点为root,再跑两边树形DP,一遍强制不选root,然后跑普通树形DP。另一遍强制选root,然后跑树形DP加上特判不能选x,最后两种答案取max即可。

      我这样写可能比较长,别人的做法只要写两遍dfs,我需要3遍(不过后面两个dfs可以复制粘贴)。但实际上是一个思路。别人的做法是dfs找到这个环,然后随便找条环上的边断开,然后强制这条边连接的两个点其中一个不选(其实就和我的写法一样的意思,只不过我是先把这两个点中的一个指定为了root)

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 1000100
      5 #define ac 2000100
      6 #define LL long long
      7 int n, m, root;
      8 int s[AC], p[AC], deep[AC];
      9 LL f[AC][2], g[AC][2], ans;//存下每个点的厌恶对象以方便判断
     10 int Head[AC], date[ac], Next[ac], tot;
     11 bool z[AC], vis[AC], book[AC], flag;
     12 
     13 inline int read()
     14 {
     15     int x = 0;char c = getchar();
     16     while(c > '9' || c < '0') c = getchar();
     17     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     18     return x;
     19 }
     20 
     21 inline void add(int f, int w)
     22 {
     23     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
     24     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
     25 }
     26 
     27 void pre()
     28 {
     29     n = read();
     30     for(R i = 1; i <= n; i ++)
     31     {
     32         s[i] = read(), p[i] = read();
     33         add(i, p[i]);
     34     }
     35     deep[1] = 1;
     36 }
     37 
     38 void dfs1(int x)//找到反向边的那个点定为root
     39 {
     40     z[x] = true;
     41     int now;
     42     //if(root) return ;
     43     for(R i = Head[x]; i; i = Next[i])
     44     {
     45         now = date[i];
     46         if(z[now] && deep[now] + 1 != deep[x]) root = x;
     47         if(z[now] && p[now] == x && p[x] == now) root = x, flag = true;//特殊情况
     48         //if(root) return ;
     49         if(z[now]) continue;
     50         deep[now] = deep[x] + 1;
     51         dfs1(now);
     52     }
     53 }
     54 
     55 void dfs2(int x)//强制选root
     56 {
     57     int now;
     58     vis[x] = true;
     59     f[x][1] = s[x];//初始化
     60     for(R i = Head[x]; i; i = Next[i])
     61     {
     62         now = date[i];
     63         if(vis[now]) continue;
     64         if(now == p[x] && x == root && !flag) continue;
     65         dfs2(now);//忽略这条边,如果是特殊情况则不能忽略,因为是重边,一旦忽略将忽略2条
     66         f[x][1] += f[now][0];
     67         f[x][0] += max(f[now][0], f[now][1]);
     68     }
     69     if(x == p[root]) f[x][1] = f[x][0];
     70 }
     71 
     72 void dfs3(int x)//强制不选root
     73 {
     74     int now;
     75     g[x][1] = s[x];
     76     book[x] = true;
     77     for(R i = Head[x]; i; i = Next[i])
     78     {
     79         now = date[i];
     80         if(book[now]) continue;
     81         if(now == p[x] && x == root && !flag) continue;
     82         dfs3(now);
     83         g[x][1] += g[now][0];
     84         g[x][0] += max(g[now][0], g[now][1]);
     85     }
     86 }
     87 
     88 void work()
     89 {
     90     for(R i = 1; i <= n; i ++)//因为本来就不一定联通,所以要跑多次
     91         if(!z[i]) 
     92         {
     93             dfs1(i); 
     94             dfs2(root);
     95             dfs3(root);
     96             ans += max(f[root][1], g[root][0]);
     97         }
     98     printf("%lld
    ", ans);
     99 }
    100 int main()
    101 {
    102 //    freopen("in.in", "r", stdin);
    103     pre();
    104     work();
    105 //    fclose(stdin);
    106     return 0;
    107 }
  • 相关阅读:
    C#中KeyDown和KeyPress区别
    c#快捷键设置和text输入限制
    问题总结
    c#串口编程和单片机通信重大发现
    c#类似单片机的8bit或运算
    c#中将默认常量(32bit)转换为8bit
    我的秋季个人阅读计划
    学期总结
    阅读笔记《软件秘籍》03
    阅读笔记--09
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9598091.html
Copyright © 2011-2022 走看看