POJ - 3659-1463(树形dp或最小支配集 点覆盖)

题目:click
问最小需要多少点与树中所有点都有联系。
最小支配集:对于图G = <v, e> 来说,最小支配集指的是从 v 中取最少的点组成一个集合,使得 V 中剩余的点都与取出来的点有边相连。设 v0是图的一个支配集,则对于图中的任意一个顶点 u ,要么属于集合 v0, 要么与 v0 中的顶点相邻. 在 v0中除去任何元素后v0 不再是支配集, 则支配集 v0 是极小支配集。

同理最小点覆盖的话,就是包揽的全部的边:
(这里附上例题:poj-1463)最小点覆盖。也就只有两状态具体见最后贴出代码。

用最小支配集去做的话建立状态:
dp[i][0]:表示不是支配集中的点,他的父节点是支配集中的点。
dp[i][1]:表示该点是支配集。
dp[i][2]:表示不是支配集中的点,有子节点是。
(直接用图的最小支配集贪心也是可以)
题目中已经已经申明为树相互联系。
直接树形dp可建立状态转移方程:
dp[i][0]:表示该节点的父亲节点已经染色,该点可染色或者不染色。
dp[i][1]:表示该点直接染色,那么子节点和父节点可选择染色或者不染色。
dp[i][2]:表示该点的子节点染色,可是子节点很多我需要去确定一个最优的子节点。(该点为子节点的父节点确定不染色,所以有两种情况去判断)
见代码:

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n;
vector<int>hh[10011];
int dp[10011][3];
bool vis[10011];
void dfs(int root,int f)
{
    if(root!=1&&hh[root].size()==1)
    {
        dp[root][2]=1e9+9;
        dp[root][0]=0;
        dp[root][1]=1;
        return ;
    }
    dp[root][1]=1;
    int MIN=1e9+10;
    bool flag=false;
    for(int i=0;i<hh[root].size();i++)
    {
        int v=hh[root][i];
        if(v==f)
            continue;
        dfs(v,root);
        dp[root][0]+=min(dp[v][1],dp[v][2]);//父亲节点如果已经染色  子节点可以染色 也可以不染色
        dp[root][1]+=min(dp[v][0],min(dp[v][1],dp[v][2]));//该节点如果已经染色 子节点父节点都可以选择染或者不染
        if(dp[v][1]>dp[v][2])//我有很多子节点我需要确定一个最优的子节点
        {
            dp[root][2]+=dp[v][2];
            MIN=min(MIN,dp[v][1]-dp[v][2]);
        }
        else
        {
            flag=true;
            dp[root][2]+=dp[v][1];
        }
    }
    if(!flag)//如果由于子节点的子节点染色情况比它好 没有一个子节点被染色了 必须要一个最好的来染色
    {
        dp[root][2]+=MIN;
    }
}
int main()
{
    memset(dp,0,sizeof(dp));
    int i,j,k;
    scanf("%d",&n);
    for(i=1;i<n;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        hh[x].push_back(y);
        hh[y].push_back(x);
    }
    if(n==1||n==2)
    {
        printf("1\n");
        return 0;
    }
    dfs(1,-1);
    printf("%d",min(dp[1][1],dp[1][2]));//因为已经把1看做一个根了
    return 0;
}

poj-1463

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n;
vector<int>hh[10011];
int dp[10011][2];
bool vis[10011];
int f[1600];
void dfs(int root)
{
    if(vis[root])
        return ;
    vis[root]=true;
    dp[root][0]=0;
    dp[root][1]=1;
    bool flag=false;
    for(int i=0;i<n;i++)
    {
        if(f[i]==root)
        {
            dfs(i);
            dp[root][1]+=min(dp[i][0],dp[i][1]);
            dp[root][0]+=dp[i][1];
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
            memset(dp,0,sizeof(dp));
            memset(f,-1,sizeof(f));
            memset(vis,false,sizeof(vis));
            int i,j,k;
            for(i=0;i<n;i++)
            {
                int xh,y;
                int num;
                scanf("%d:(%d)",&xh,&num);
                for(j=0;j<num;j++)
                {
                    scanf("%d",&y);
                    f[y]=xh;
                }
            }
            if(n==1)
            {
                printf("0\n");
                continue;
            }
            int root;
            for(i=0;i<n;i++)
            {
                if(f[i]==-1)
                    root=i;
            }
            dfs(root);
            printf("%d\n",min(dp[root][0],dp[root][1]));
    }
    return 0;
}

发布了72 篇原创文章 · 获赞 19 · 访问量 7503

猜你喜欢

转载自blog.csdn.net/weixin_43958964/article/details/104433270