[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 }
原文地址:https://www.cnblogs.com/ww3113306/p/9598091.html