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
  • 相关阅读:
    Free Energies: MMPBSA
    蛋白添加ACE,NME
    生物信息学在线软件集锦
    Developing Nonstandard Parameters:MCPB.py Example
    python锁定焦点到指定进程窗口的参考方法
    【Windows程序设计】Unicode简介
    SecureCRT连接linux终端中文显示乱码解决办法
    ubuntu下音箱有声音,耳机没声音之类问题解决办法
    python处理不同编码格式的文件
    【Windows程序设计】第一个Windows程序
  • 原文地址:https://www.cnblogs.com/Rlemon/p/3265530.html
Copyright © 2011-2022 走看看