zoukankan      html  css  js  c++  java
  • 树上的DP

    CF#196B http://codeforces.com/contest/338/problem/B

    题意:在一颗树上,给m个点,求到所有m个点距离不超过d的点的个数,所有路径长度为1。

    分析:问题转化,就是求一个点是否到离它最远的给定点的距离小于d,

    先第一遍dfs1求出以当前节点u为根的子树到子树中离它最远的给定点的距离d1[u],和次远的距离d2[u],

    并且记录下最远距离是从哪个son转移过来的,fo[u];

    第二遍dfs2求出当前节点从它fa出发能碰到给定点的最远距离up[u],那么对于当前点u,max(d1[u],up[u]),就是u到

    所有给定点的最远的距离;

    up[]的可以按照dfs的顺序递推求出,假设我们已经求出up[c1],现在求up[c2]

    up[c2]表示从c2的fa结点也就是c1那边给定结点(红色圆点)到c2的最远距离

    那么up[c2] 由两部分转移过来,一个是原先以c1为根的子树除去c2的分支后的最远距离,这就是为什么要记录最远是从哪个son转移过来

    dd = (fo[c1] == c2 ? d2[c1] : d1[c1]);

    还有就是up[c1];显然up[c2] = max(dd,up[c1])+1;(up[c1]表示从c6那边转移过来的值)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<vector>
     7 #include<cmath>
     8 #define pbk push_back
     9 using namespace std;
    10 const int N = 100000+10;
    11 const int INF = 1000000+10;
    12 vector<int> g[N];
    13 int n,m,d;
    14 bool p[N];
    15 int d1[N],d2[N],fo[N],up[N];
    16 int co[N];
    17 void update(int c,int rt,int x,int &m1,int &m2) {
    18     if (x >= m1) {
    19         m2 = m1; m1 = x;
    20         fo[rt] = c;
    21     }else if (x < m1) {
    22         if (x > m2) m2 = x;
    23     }
    24 }
    25 void dfs1(int rt,int fa) {
    26     int sz = g[rt].size();
    27     d1[rt] = d2[rt] = -INF;
    28     for (int i = 0; i < sz; i++) {
    29         int c = g[rt][i];
    30         if (c == fa) continue;
    31         dfs1(c,rt);
    32         if (co[c]) {
    33             update(c,rt,d1[c]+1,d1[rt],d2[rt]);
    34             co[rt] = 1;
    35         }
    36     }
    37     if (p[rt]) {
    38         if (d1[rt] > 0 && d2[rt] < 0) d2[rt] = 0;
    39         if (d1[rt] < 0) {
    40             d1[rt] = 0; fo[rt] = rt;
    41         }
    42         co[rt] = 1;
    43     }
    44 }
    45 void dfs2(int rt,int fa) {
    46     int sz = g[rt].size();
    47     for (int i = 0; i < sz; i++) {
    48         int c = g[rt][i];
    49         if (c == fa) continue;
    50         int dd = (fo[rt] == c ? d2[rt] : d1[rt]);
    51         up[c] = max(dd, up[rt]) + 1;
    52         dfs2(c,rt);
    53     }
    54 }
    55 void solve(){
    56     memset(co,0,sizeof(co));
    57     memset(fo,-1,sizeof(fo));
    58     dfs1(1,-1);
    59     up[1] = -INF;
    60     dfs2(1,-1);
    61   
    62     int ans = 0;
    63     for (int i = 1; i <= n; i++) {
    64         if (d1[i] <= d && up[i] <= d) ans++;
    65     }
    66     printf("%d
    ",ans);
    67 }
    68 int main(){
    69     while (~scanf("%d%d%d",&n,&m,&d)) {
    70         memset(p,0,sizeof(p));
    71         for (int i = 0; i < m; i++) {
    72             int f; scanf("%d",&f);
    73             p[f] = 1;
    74         }
    75         for (int i = 0; i <= n; i++) g[i].clear();
    76         for (int i = 0; i < n-1; i++) {
    77             int u,v; scanf("%d%d",&u,&v);
    78             g[u].pbk(v); g[v].pbk(u);
    79         }
    80         solve();
    81     }
    82     return 0;
    83 }
    View Code

    hdu4679 http://acm.hdu.edu.cn/showproblem.php?pid=4679

    题意:给n个结点的树,每条边上都有一个w,去掉一条边后变成两颗树,选一条边使得w*max(diaA,diaB);其中diaA,diaB表示树的直径(树上最长路);

    分析:删掉一条边后,分成两部分,一部分是原先的子树,对于所有子树我们可以一遍dfs求出它的dia,问题我们该怎么求出

    另外一部分的dia,设dia[u]表示从u的fa那一边能得到的最大直径,dis[u]表示u的fa那边能得到的最大距离,

    那么这一部分的dia就是max(dia[u],dis[u]+1+md, dd);其中md表示删掉边后以u为根的子树叶子节点到u的最远距离,

    dd表示删掉边后以u为根的子树的直径;这样就可以求出删掉一条边后的解了;

    dd是B部分,dis[u]+1+md是A,B和起来的部分,dia[u]表示A部分;

    接着就是该如何求dia[u], dis[u];

    dis[u]的求法和上一题一样,dia[u]的求法也差不多

    在第一遍dfs的时候保留下以u为根的子树到u的最大,次大,次次大的距离,并且保存下最大,次大是从哪个son转移过来的,

    这样就可以再第二遍dfs的时候递推求出dia[u],dis[u];

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<vector>
     8 #include<set>
     9 #pragma comment(linker, "/STACK:1024000000,1024000000")
    10 #define pbk push_back
    11 using namespace std;
    12 const int N = 100000+10;
    13 const int INF = 1000000+10;
    14 struct edge{
    15     int v,w,id;
    16     edge(int v,int w,int id):v(v),w(w),id(id){}
    17 };
    18 vector<edge> g[N];
    19 int n;
    20 int md1[N],md2[N],md3[N],fro1[N],fro2[N];
    21 void update(int x,int c,int rt) {
    22     if (x >= md1[rt]) {
    23         md3[rt] = md2[rt];
    24         md2[rt] = md1[rt]; fro2[rt] = fro1[rt];
    25         md1[rt] = x; fro1[rt] = c;
    26     }else if (x >= md2[rt]){
    27         md3[rt] = md2[rt];
    28         md2[rt] = x; fro2[rt] = c;
    29     }else if (x > md3[rt]) md3[rt] = x;
    30 }
    31 void dfs1(int rt,int fa) {
    32     int sz = g[rt].size();
    33     md1[rt] = md2[rt] = md3[rt] = 0;
    34     for (int i = 0; i < sz; i++) {
    35         edge c = g[rt][i];
    36         if (c.v == fa) continue;
    37         dfs1(c.v,rt);
    38         update(md1[c.v]+1,c.v,rt);
    39     }
    40 }
    41 int dis[N],dia[N];
    42 int an1,an2;
    43 void dfs2(int rt,int fa) {
    44     int sz = g[rt].size();
    45     for (int i = 0; i < sz; i++) {
    46         edge c = g[rt][i];
    47         if (c.v == fa) continue;
    48         int dd ,md;
    49         if (fro1[rt] == c.v) {
    50             dd = md2[rt] + md3[rt]; md = md2[rt];
    51         }else if (fro2[rt] == c.v) {
    52             dd = md1[rt] + md3[rt]; md = md1[rt];
    53         } else {
    54             dd = md1[rt] + md2[rt]; md = md1[rt];
    55         }
    56         dd = max(dd, max(dia[rt],dis[rt] + 1 + md));
    57         dia[c.v] = dd;
    58         dd = max(dd, md1[c.v] + md2[c.v]);
    59         dis[c.v] = max(md, dis[rt] + 1);
    60         if (an1 == -1) {
    61             an1 = c.id; an2 = dd * c.w;
    62         }else if (dd * c.w < an2) {
    63             an1 = c.id; an2 = dd * c.w;
    64         }else if (dd * c.w == an2 && c.id < an1) {
    65             an1 = c.id;
    66         }
    67 
    68         dfs2(c.v,rt);
    69     }
    70 }
    71 void solve(){
    72     dfs1(1,-1);
    73     dia[1] = -INF; dis[1] = -INF;
    74     an1 = -1;
    75     dfs2(1,-1);
    76     printf("%d
    ",an1);
    77 }
    78 int main(){
    79     int T,cas = 0; scanf("%d",&T);
    80     while (T--) {
    81         scanf("%d",&n);
    82         for (int i = 0; i <= n; i++) g[i].clear();
    83         for (int i = 1; i < n; i++) {
    84             int u,v,w; scanf("%d%d%d",&u,&v,&w);
    85             g[u].pbk(edge(v,w,i));
    86             g[v].pbk(edge(u,w,i));
    87         }
    88         printf("Case #%d: ",++cas);
    89         solve();
    90     }
    91     return 0;
    92 }
    View Code

     hdu 4661 http://acm.hdu.edu.cn/showproblem.php?pid=4661

    题意:n个,他们之间的关系是一棵树,每个人都有一个消息,每次可以选一个人把他知道的所有消息传给另一个人,问

    所有人知道所有消息的方案数,假设要传k次,其中只要有一次传出的人或接受的人不同都算不同;

    分析:每个人至少要把自己的消息传出去,至少要接受一次消息,很明显,确定树根后只要从叶子节点一层一层传上来

    直到跟然后再从跟传下去,一共(n-1)*2,是最优的;求方案数,首先是从叶子节点传到根,其实就是所有节点的拓扑序的个数;

    设f[u]表以u为根节点的子树的拓扑序的总数num[u]表示子树的结点数,

    那么f[u] = f[son1] * f[son2] * f[soni] * (num[u]-1)!/(num[son1]! * num[son2]! * num[soni] ! );

    通过一遍dfs在确定根后,可以求出所有子树的f[],在第二遍dfs的时候通过维护从当前结点的fa那边的拓扑序列,和num[]值

    递推求出以当前结点为树根的最终方案; 

    然后是根传到叶子结点是等价于从叶子结点传到根的,所有累加所有方案数的平方就是最终解;

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<vector>
     8 #pragma comment(linker, "/STACK:1024000000,1024000000") 
     9 #include<set>
    10 using namespace std;
    11 typedef long long LL;
    12 const int N = 1000000+10;
    13 const int Mod = 1e9+7;
    14 vector<int> g[N];
    15 int n;
    16 LL ifac[N],fac[N];
    17 void ex_gcd(LL a, LL b, LL &g, LL &x, LL &y) {
    18     if (b == 0) {
    19         g = a; x = 1; y = 0;
    20     }else {
    21         ex_gcd(b, a%b, g, y, x);
    22         y -= x * (a/b);
    23     }
    24 }
    25 LL inv(LL a, LL n) {
    26     LL x,y,g;
    27     ex_gcd(a,n,g,x,y);
    28     return g == 1 ? (x % n + n) % n : -1;
    29 }
    30 void init(){
    31     fac[0] = 1; ifac[0] = 1;
    32     for (int i = 1; i < N; i++) {
    33         fac[i] = fac[i-1] * i % Mod;
    34         ifac[i] = inv(fac[i],Mod);
    35     }
    36 }
    37 int num[N],f[N];
    38 void dfs1(int rt,int fa) {
    39     int sz = g[rt].size();
    40     num[rt] = 0; f[rt] = 1;
    41     for (int i = 0; i < sz; i++) {
    42         int c = g[rt][i];
    43         if (c == fa) continue;
    44         dfs1(c,rt);
    45         num[rt] += num[c];
    46         f[rt] = (LL)f[rt] * f[c] % Mod * ifac[num[c]] % Mod;
    47     }
    48     num[rt] += 1;
    49     f[rt] = (LL)f[rt] * fac[num[rt] - 1] % Mod;
    50 }
    51 LL ans;
    52 int up[N],cn[N];
    53 void dfs2(int rt,int fa) {
    54     int sz = g[rt].size();
    55     for (int i = 0; i < sz; i++) {
    56         int c = g[rt][i];
    57         if (c == fa) continue;
    58 
    59         LL tmp = (LL)f[rt] * ifac[num[rt] - 1] % Mod * fac[num[c]] % Mod * inv(f[c],Mod) % Mod ;
    60         tmp = tmp * up[rt] % Mod * ifac[n - num[rt]] % Mod * fac[n - num[c] - 1] % Mod;
    61         up[c] = tmp;
    62 
    63         LL tp = f[c] * ifac[num[c] - 1] % Mod  * tmp % Mod * ifac[n - num[c]] % Mod * fac[n-1] % Mod;
    64         ans = (ans +  tp * tp) % Mod;
    65         dfs2(c,rt);
    66     }
    67 }
    68 void solve(){
    69     dfs1(1,-1);
    70     ans = (LL)f[1] * f[1] % Mod;
    71     up[1] = 1;
    72     dfs2(1,-1);
    73     printf("%I64d
    ",ans);
    74 }
    75 int main(){
    76     init();
    77     int T; scanf("%d",&T);
    78     while (T--) {
    79         scanf("%d",&n);
    80         for (int i = 0; i <= n; i++) g[i].clear();
    81         for (int i = 0; i < n-1; i++) {
    82             int u,v; scanf("%d%d",&u,&v);
    83             g[u].push_back(v); g[v].push_back(u);
    84         }
    85         solve();
    86     }
    87     return 0;
    88 }
    View Code

    hdu 4276 http://acm.hdu.edu.cn/showproblem.php?pid=4276

    题意:给你一棵树,路径上有一个花费,每个节点有一个价值,问在T时间内,从1走到n能得到的最大价值;

    分析:首先因为在树上,所有从1到n的路径是唯一的,也就是唯一路径上的边我们只会走一次,然后我们可以想象

    如果把1和n结点拉直后,会变成一个森林并且树的根都是在路径上的,并且如果要去拿那里的价值每条路径都要经历2边;

    思路就很明了,先预处理出所有路径上的点,然后以路径上的结点为子树根求出以每个根花费j能得到的最大代价,

    最后就是分组背包,每组选一个;

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<cstring>
     7 #include<vector>
     8 #define mp make_pair
     9 #define pbk push_back
    10 using namespace std;
    11 const int N = 100+10;
    12 typedef pair<int,int> pii;
    13 int dp[N][500+10];
    14 int n,T;
    15 vector<pii> G[N];
    16 int val[N];
    17 int On[N], f, sti;
    18 void dfs_find(int u,int fa,int dep) {
    19     int sz = G[u].size();
    20     On[u] = 1;
    21     if (u == n) {
    22         f = 1;
    23         return;
    24     }
    25     for (int i = 0; i < sz; i++) {
    26         int v = G[u][i].first;
    27         if (v == fa) continue;
    28         sti = sti + G[u][i].second;
    29         dfs_find(v,u,dep+1);
    30         if (f) return;
    31         sti = sti - G[u][i].second;
    32     }
    33     On[u] = 0;
    34 }
    35 void dfs(int u,int fa) {
    36     int sz = G[u].size();
    37     for (int i = 0; i <= T - sti; i++) dp[u][i] = val[u];
    38     for (int i = 0; i < sz; i++) {
    39         int v = G[u][i].first;
    40         if (On[v] || v == fa) continue;
    41         dfs(v, u);
    42         int ti = G[u][i].second * 2;
    43         for (int j = T - sti; j >= 0; j--) {
    44             for (int k = 0; k <= j - ti; k++) {
    45                 dp[u][j] = max(dp[u][j], dp[u][j - k - ti] + dp[v][k]);
    46             }
    47         }
    48     }
    49 
    50 }
    51 int an[500+10];
    52 void solve(){
    53     memset(On,0,sizeof(On));
    54     f = 0; sti = 0;
    55     dfs_find(1,-1,0);
    56     if (sti > T) {
    57         puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!");
    58         return;
    59     }
    60     memset(dp,0,sizeof(dp));
    61     for (int i = 1; i <= n; i++) {
    62         if (On[i]) {
    63           dfs(i,-1);
    64         }
    65     }
    66     //分组背包 
    67     memset(an,0,sizeof(an));
    68     for (int i = 1; i <= n; i++) if (On[i]){
    69         for (int j = T - sti; j >= 0; j--) {
    70             for (int k = 0; k <= j; k++) {
    71                 an[j] = max(an[j], an[j - k] + dp[i][k]);
    72             }
    73         }
    74     }
    75     int idx = 0;
    76     for (int i = 0; i <= T - sti; i++) {
    77         if (an[i] > an[idx]) idx = i;
    78     }
    79     printf("%d
    ",an[idx]);
    80 
    81 }
    82 int main(){
    83     while (~scanf("%d%d",&n,&T)) {
    84         for (int i = 0; i <= n; i++) G[i].clear();
    85         for (int i = 0; i < n-1; i++) {
    86             int u,v,w; scanf("%d%d%d",&u,&v,&w);
    87             G[u].pbk(mp(v,w)); G[v].pbk(mp(u,w));
    88         }
    89         for (int i = 1; i <= n; i++) scanf("%d",val+i);
    90         solve();
    91     }
    92     return 0;
    93 }
    View Code

     hdu 4044

    题意:可以在结点上建至多一个炮台,花费已知,攻击力已知,求只有m费用的情况下从结点1到叶子结点的路径上攻击力之和的最小值最大;

    分析:对于结点1,首先我们要分配m使得它的各个son的攻击力之和最小值经历大,dp[u][m]表示的就是以结点u为子树根花费m能得到的对于该子树的最小值最大;

    现在的问题就是这么推出dp[u][m]; 显然 对已son1来说dp[u][m] = dp[son1][m]; 对于son2: tp = max(tp,min(dp[u][m-k], dp[son2][k])); dp[u][m] = tp;此时的dp[u][m-k]表示的前i个son都取完之后最小的攻击力最大的值;  当遍历完son之后,那么再加上u位置能建立的炮台,就是最终的答案;

    trick : 因为花费有为zero的,所有直接dp[u][m] = max(dp[u][m], dp[u][m-x] + y);是有问题的,当存在两次以上花费为0的数据就会错误,所以要预先处理处理花费为i最大攻击力;

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<iostream>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<cstdlib>
      7 #include<vector>
      8 #include<map>
      9 #define pbk push_back
     10 #define mp make_pair
     11 using namespace std;
     12 const int N = 1000+10;
     13 typedef pair<int,int> pii;
     14 int dp[N][200+10];
     15 int n,m;
     16 vector<int> g[N];
     17 vector<pii> tow[N];
     18 int tp[N][200+10];
     19 void dfs(int u,int fa) {
     20     bool first = true;
     21     for (int i = 0; i < g[u].size(); i++) {
     22         int  v = g[u][i];
     23         if (v == fa) continue;
     24         dfs(v,u);
     25         if (first) {
     26             for (int j = 0; j <= m; j++) dp[u][j] = dp[v][j];
     27             first = false;
     28             continue;
     29         }
     30         for (int j = m; j >= 0; j--) {
     31             int tp = 0;
     32             for (int k = 0; k <= j; k++) {
     33                 int tmp = min (dp[u][j-k], dp[v][k]);
     34                 //if (first) tmp = dp[v][k];
     35                 tp = max(tp, tmp);
     36             }
     37             dp[u][j] = tp;
     38         }
     39         //first = false;
     40     }
     41 
     42     for (int i = m; i >= 0; i--) {
     43        /* for (int j = 0; j < tow[u].size(); j++) {
     44              int x = tow[u][j].first, y = tow[u][j].second;
     45              dp[u][i] = max(dp[u][i], dp[u][i-x] + y);
     46         }*/
     47         for (int j = 0; j <= i; j++) dp[u][i] = max(dp[u][i], dp[u][i - j] + tp[u][j]);
     48     }
     49 }
     50 void solve() {
     51     for (int i = 1; i <= n; i++) {
     52         for (int j = 1; j <= m; j++) tp[i][j] = max(tp[i][j], tp[i][j-1]);
     53     }
     54     memset(dp, 0, sizeof(dp));
     55     dfs(1,-1);
     56     int idx = 0;
     57     for (int i = 0; i <= m; i++) {
     58         if (dp[1][i] > dp[1][idx]) idx = i;
     59     }
     60     printf("%d
    ",dp[1][idx]);
     61 }
     62 int main(){
     63     int T; scanf("%d",&T);
     64     while (T--) {
     65         scanf("%d",&n);
     66         for (int i = 0; i <= n; i++) g[i].clear();
     67 
     68         for (int i = 0; i < n-1; i++) {
     69             int u,v; scanf("%d%d",&u,&v);
     70             g[u].pbk(v); g[v].pbk(u);
     71         }
     72         scanf("%d",&m);
     73         memset(tp,0,sizeof(tp));
     74         for (int i = 1; i <= n; i++) {
     75             int c; scanf("%d",&c);
     76             tow[i].clear();
     77             for (int j = 0; j < c; j++) {
     78                 int u,v; scanf("%d%d",&u,&v);
     79          //       tow[i].pbk(mp(u,v));
     80                 tp[i][u] = max(tp[i][u],v);
     81             }
     82         }
     83 
     84         solve();
     85 
     86     }
     87 
     88     return 0;
     89 }
     90 
     91 /*
     92 2
     93 2
     94 1 2
     95 30
     96 3 10 20 20 40 30 50
     97 3 10 30 20 40 30 45
     98 4
     99 2 1
    100 3 1
    101 1 4
    102 9
    103 3 10 20 20 40 30 50
    104 3 10 30 20 40 30 45
    105 3 10 30 20 40 30 35
    106 3 10 30 20 40 30 35
    107 
    108 */
    View Code

     CF 219D

    题意:一棵树,路径是由方向的,选一个节点,使得旋转最少的边使得从该结点到所有结点都有路;

    分析:如果路径分别标记为1,-1那么就是统计以某个结点为根,-1的最小个数,dp[u]表示以u为根的子树下有多少个-1;

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<iostream>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<vector>
     8 #include<set>
     9 #define pbk push_back
    10 #define mp make_pair
    11 using namespace std;
    12 const int N = 200000 + 10;
    13 typedef pair<int,int> pii;
    14 vector<pii> g[N];
    15 int n;
    16 int dp[N];
    17 vector<int> an;
    18 int up;
    19 void dfs1(int u,int fa) {
    20     int sz = g[u].size();
    21     dp[u] = 0;
    22     for (int i = 0; i < sz; i++) {
    23         int v = g[u][i].first;
    24         if (v == fa) continue;
    25         dfs1(v,u);
    26         dp[u] += dp[v] + (g[u][i].second == -1 ? 1 : 0);
    27     }
    28 }
    29 void update(int u,int v) {
    30     if (u < up) {
    31         up = u;
    32         an.clear(); an.pbk(v);
    33     }else if ( u == up ) an.pbk(v);
    34 }
    35 void dfs2(int u,int fa,int ba) {
    36     int sz = g[u].size();
    37 
    38     for (int i = 0; i < sz; i++) {
    39         int v = g[u][i].first;
    40         if (v == fa) continue;
    41         int tp = dp[u] - dp[v] - (g[u][i].second == -1 ? 1 : 0) + ba + (g[u][i].second == 1 ? 1 : 0);
    42         update(dp[v] + tp,v);
    43         dfs2(v,u,tp);
    44     }
    45 }
    46 void solve(){
    47     memset(dp,0,sizeof(dp));
    48     dfs1(1,-1);
    49     up = dp[1];
    50     an.clear();
    51     an.pbk(1);
    52     dfs2(1,-1,0);
    53     printf("%d
    ",up);
    54     sort(an.begin(),an.end());
    55     for (int i = 0; i < an.size(); i++) {
    56         printf("%d%c",an[i],i == an.size() - 1 ? '
    ' : ' ');
    57     }
    58 }
    59 int main() {
    60     while (~scanf("%d",&n)) {
    61         for (int i = 0; i <= n; i++) g[i].clear();
    62         for (int i = 0; i < n-1; i++) {
    63             int u,v;  scanf("%d%d",&u,&v);
    64             g[u].pbk(mp(v,1)); g[v].pbk(mp(u,-1));
    65         }
    66         solve();
    67     }
    68     return 0;
    69 }
    View Code

    CF 120F http://codeforces.com/problemset/problem/120/F

    题意:给你n棵树,累加每棵树的直径;

    分析:直接求;该题CF上是文件读入的;

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdlib>
     6 #include<cmath>
     7 #include<vector>
     8 #define pbk push_back
     9 #define mp make_pair
    10 using namespace std;
    11 typedef pair<int,int> pii;
    12 const int N = 100+10;
    13 vector<int> g[N];
    14 int n;
    15 int an;
    16 int dia[N],di1[N],di2[N],fo[N],mxdia;
    17 void update(int x,int u,int v) {
    18     if (x > di1[u]) {
    19         di2[u] = di1[u];
    20         di1[u] = x;
    21         fo[u] = v;
    22     }else if (x == di1[u] && x > di2[u]) di2[u] = x;
    23 }
    24 void dfs1(int u,int fa) {
    25     int sz = g[u].size();
    26     di1[u] = di2[u] = 0;
    27     for (int i = 0; i < sz; i++) {
    28         int v = g[u][i];
    29         if (v == fa) continue;
    30         dfs1(v,u);
    31         update(di1[v] + 1,u,v);
    32     }
    33     dia[u] = di1[u] + di2[u];
    34 }
    35 void dfs2(int u,int fa,int up) {
    36     int sz = g[u].size();
    37     for (int i = 0; i < sz; i++) {
    38         int v = g[u][i];
    39         if (v == fa) continue;
    40         int tp = max((fo[u] == v ? di2[u] : di1[u]) , up ) + 1;
    41         mxdia = max(mxdia, di1[v] + tp);
    42         dfs2(v,u,tp);
    43     }
    44 }
    45 void solve(){
    46     dfs1(1,-1);
    47     mxdia = dia[1];
    48     dfs2(1,-1,0);
    49     an += mxdia;
    50 }
    51 int main(){
    52     int c;
    53     freopen("input.txt","r",stdin);
    54     freopen("output.txt","w",stdout);
    55     while (~scanf("%d",&c)) {
    56         an = 0;
    57         while (c--) {
    58             scanf("%d",&n);
    59             for (int i = 0; i <= n; i++) g[i].clear();
    60             for (int i = 0; i < n-1; i++) {
    61                 int u,v; scanf("%d%d",&u,&v);
    62                 g[u].pbk(v); g[v].pbk(u);
    63             }
    64             solve();
    65         }
    66         printf("%d
    ",an);
    67     }
    68     return 0;
    69 }
    View Code

     CF 212E 

    题意:一棵树上放两种餐厅,不同的餐厅不能相邻,每种餐厅至少一个,问最多能放几个,并且输出所有可行方案;

    分析:显然对于一棵上来说,肯定可以选出一个节点,它有至少两个分支,所以最多的放置数为n-1

    对于方案数,我们可以枚举结点,然后对于至少有两个分支的结点进行背包,求出第一中餐厅能放的值,那么所有答案并就是方案;

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<cstdlib>
     7 #include<vector>
     8 #define pbk push_back
     9 using namespace std;
    10 const int N = 50000+10;
    11 vector<int> g[N];
    12 int n;
    13 int an[N];
    14 int dp[N];
    15 
    16 void dfs1(int u,int fa) {
    17     int sz = g[u].size();
    18     dp[u] = 1;
    19     for (int i = 0; i < sz; i++) {
    20         int v = g[u][i];
    21         if (v == fa) continue;
    22         dfs1(v,u);
    23         dp[u] += dp[v];
    24     }
    25 }
    26 void dfs2(int u,int fa,int up) {
    27     int sz = g[u].size();
    28     bool tmp[N];
    29     memset(tmp,0,sizeof(tmp));
    30     tmp[0] = 1;
    31     int cnt = 0;
    32     for (int i = 0; i < sz; i++) {
    33         int v = g[u][i];
    34         if (v == fa) continue;
    35         cnt++;
    36         dfs2(v,u,up + dp[u] - dp[v]);
    37         for (int j = n; j >= dp[v]; j--) {
    38             if (tmp[j - dp[v]] == 1) tmp[j] = 1;
    39         }
    40     }
    41     for (int j = n; j >= up; j--) {
    42         if (tmp[j - up]) tmp[j] = 1;
    43     }
    44     if (up == 0 && cnt == 1) return;
    45     for (int j = n; j >= 0; j--) {
    46         if (tmp[j]) an[j] = 1;
    47     }
    48 }
    49 void solve() {
    50     memset(an,0,sizeof(an));
    51     dfs1(1,-1);
    52 
    53     dfs2(1,-1,0);
    54     int cnt = 0;
    55     for (int i = 1; i < n-1; i++) if (an[i]) cnt++;
    56     printf("%d
    ",cnt);
    57     for (int i = 1; i < n-1; i++) {
    58         if (an[i]) printf("%d %d
    ",i,n-1-i);
    59     }
    60 }
    61 int main(){
    62     while (~scanf("%d",&n)) {
    63         for (int i = 0; i <= n; i++) g[i].clear();
    64         for (int i = 0; i < n-1; i++) {
    65             int u,v; scanf("%d%d",&u,&v);
    66             g[u].pbk(v); g[v].pbk(u);
    67         }
    68         solve();
    69     }
    70     return 0;
    71 }
    View Code
  • 相关阅读:
    Java实现 蓝桥杯 算法提高 特等奖学金(暴力)
    Java实现 蓝桥杯 算法提高 特等奖学金(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    第一届云原生应用大赛火热报名中! helm install “一键安装”应用触手可及!
    云原生时代,2个方案轻松加速百万级镜像
    Knative 基本功能深入剖析:Knative Serving 自动扩缩容 Autoscaler
  • 原文地址:https://www.cnblogs.com/Rlemon/p/3265530.html
Copyright © 2011-2022 走看看