zoukankan      html  css  js  c++  java
  • [bzoj] 1040 骑士 || 基环外向树dp

    原题

    给出n个点n条边和每个点的点权,一条边的两个断点不能同时选择,问最大可以选多少。


    //图是一张基环外向树森林
    是不是很像舞会啊~
    就是多了一条边。
    所以我们考虑一下对于一棵基环外向树,拆掉一条在环上的边,变成一棵树。在这个树上以断边的一个断点为根,跑舞会,就得到了这棵树的最大值(根选和根不选了两种)。考虑到对于拆下来的内一条边,也要满足断点不能同时选择,所以此时得到的答案中,根不选一定是正确的,但是根选不一定是正确的(因为我们不知道此刻另一个断点是否被选择)。
    那么我们强制该点不选,然后更新至根选的状态即可。

    #include<cstdio>
    #include<algorithm>
    #define N 1000010
    typedef long long ll;
    using namespace std;
    int n,a[N],f[N],head[N],ecnt=1,cnt[N];
    ll dp[N][2],ans;
    bool vis[N];
    struct hhh
    {
        int to,next;
    }edge[N];
    
    int read()
    {
        int ans=0,fu=1;
        char j=getchar();
        for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1;
        for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
        return ans*fu;
    }
    
    void add(int u,int v)
    {
        edge[ecnt].to=v;
        edge[ecnt].next=head[u];
        head[u]=ecnt++;
    }
    
    void dfs(int x)//经典dp
    {
        vis[x]=1;
        dp[x][1]=a[x];
        for (int i=head[x];i;i=edge[i].next)
    	if (!vis[edge[i].to])
    	{
    	    dfs(edge[i].to);
    	    dp[x][0]+=max(dp[edge[i].to][0],dp[edge[i].to][1]);
    	    dp[x][1]+=dp[edge[i].to][0];
    	}
    }
    
    void DP(int x)
    {
        int root;
        for (root=x;cnt[root]!=x;root=f[root])
    	cnt[root]=x;
        dfs(root);
        x=f[root];//x为当前根,那么不在树上的那条边就是x和f[x]的边,所以强制f[x]不选
        dp[x][1]=dp[x][0];
        for (x=f[x];x!=root;x=f[x])
        {
    	dp[x][0]=0;dp[x][1]=a[x];
    	for (int i=head[x];i;i=edge[i].next)
    	{
    	    dp[x][0]+=max(dp[edge[i].to][0],dp[edge[i].to][1]);
    	    dp[x][1]+=dp[edge[i].to][0];
    	}
        }
        dp[root][1]=a[root];
        for (int i=head[root];i;i=edge[i].next)
    	dp[root][1]+=dp[edge[i].to][0];//强制根选,则+=儿子们都不选
        ans+=max(dp[root][0],dp[root][1]);//取当前基环外向树的最大值
    }
    
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++)
    	a[i]=read(),f[i]=read(),add(f[i],i);
        for (int i=1;i<=n;i++)//基环外向树森林
    	if (!vis[i]) DP(i);
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    [每日一题系列] LeetCode 1071. 字符串的最大公因子
    [每日一题系列] LeetCode 1013. 将数组分成和相等的三个部分
    git diff (19)
    WinDbg探究CLR底层(1)
    使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象
    转MySQL遇到的语法差异及解决方案
    批量拼脚本神器-NimbleText
    Visual Studio 2017中使用正则修改部分内容
    如何使用ILAsm与ILDasm修改.Net exe(dll)文件
    在Windows上安装Elasticsearch v5.4.2
  • 原文地址:https://www.cnblogs.com/mrha/p/8249673.html
Copyright © 2011-2022 走看看