Codeforces Round #603 (Div. 2) F Economic Difficulties(DP)

题目链接:https://codeforces.com/contest/1263/problem/F

题目大意:

  有两棵树,都以1为根节点,两棵树的叶子数量相同,且都分别连接一个电机,若电机存在到达任意一棵树的根节点的路径,则表示该电机可用,问最多删除多少条边仍能保持所有电机可用。

题目思路:

  qsc讲解视频传送门:传送门
  首先可以发现一个事情,那就是这是一棵树,如果让某个节点报废的话,那么这个节点一定会导致几个电机报废,也就是这个节点拥有的叶子所掌管的电机。同时树有一个很好的性质,就是删边可以转化为删点,每个边都转化为它指向的那个点,只有根节点没有这个待遇为0。那么如果删除一个点,那么它覆盖的电机就会得不到这棵树的保护,同时删除的边数就是点的个数(该点不是根的情况下)。那么,第一步的预处理就显而易见了! l [ _ ] [ i ] l[\_][i] l[_][i]表示对于第_棵树的第i个节点,控制的最左电机是哪个,r相反,这样的话通过dfs就能获得每个节点能控制的电机是那几台, v a l [ l ] [ r ] val[l][r] val[l][r]表示某一棵树l~r的电机得不到保护能删除的最多的边(因为只有一棵树这个区间的电机凉了,所以它们可以交给另一棵树帮忙顶住),val的更新刚才说过,边的数量可以转换为子树大小,就是注意根节点为0,所以只有非根节点, s z [ _ ] [ u ] sz[\_][u] sz[_][u]才初始化为1。
  然后就是dp阶段, d p [ i ] dp[i] dp[i]表示1~i最多能删几条边。这个dp转移就是n^2枚举区间,每个区间的val就表示这个区间的电机少一棵树庇佑最多能删除多少边,那么 d p [ i − 1 ] + v a l [ i ] [ j ] dp[i-1]+val[i][j] dp[i1]+val[i][j]就是1 ~ i-1最多能删除的边数加上i~j区间内最多能删除多少边,就是 d p [ i ] dp[i] dp[i]能删除多少边。
  感觉这个DP非常巧妙,结合树就更有意思了,即使现在明白了它的做法,还是觉得这个思路非常难想到,希望以后能独立做出这种DP。

以下是代码:

#include<bits/stdc++.h>

using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 2e3+5;
int n,a,x,l[2][MAXN],r[2][MAXN],sz[2][MAXN],val[MAXN][MAXN];
vector<int>v[2][MAXN];
int dp[MAXN];
void dfs(int _,int u){
    
    
    int len=v[_][u].size();
    if(u!=1)sz[_][u]=1;
    rep(i,0,len-1){
    
    
        int y=v[_][u][i];
        dfs(_,y);
        sz[_][u]+=sz[_][y];
        l[_][u]=min(l[_][u],l[_][y]);
        r[_][u]=max(r[_][u],r[_][y]);
    }
    val[l[_][u]][r[_][u]]=max(val[l[_][u]][r[_][u]],sz[_][u]);
}
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n){
    
    
        memset(dp,0,sizeof(dp));
        memset(sz,0,sizeof(sz));
        memset(val,0,sizeof(val));
        rep(_,0,1){
    
    
            cin>>a;
            rep(i,1,a)l[_][i]=n+1,r[_][i]=0;
            rep(i,1,a)v[_][i].clear();
            rep(i,2,a){
    
    
                cin>>x;
                v[_][x].push_back(i);
            }
            rep(i,1,n){
    
    
                cin>>x;
                l[_][x]=r[_][x]=i;
            }
        }
        rep(_,0,1)dfs(_,1);
        rep(i,1,n){
    
    
            rep(j,i,n){
    
    
                dp[j]=max(dp[j],dp[i-1]+val[i][j]);
            }
        }
        cout<<dp[n]<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/toohandsomeIeaseId/article/details/104129845