POJ 3342 Party at Hali-Bula(树的最大独立集)

  • 题目大意

    求树的最大独立集节点个数并判断最大独立集是否唯一
    ps:树的最大独立集是指选出尽量多的点使得两两点之间没有边相连

  • 分析

    如果不判断是否唯一这道题可以用贪心来做
    但加了这个条件之后贪心就不好判断了
    考虑动态规划的做法,用 dp[i][0] dp[i][1] 分别表示以 i 节点为根的子树i节点不选和选两种情况下的最大独立集节点数,转移方程就不用多说了。

  • 总结

    这道题输入用的是字符串表示节点,所以一开始需要用map将字符串映射成一个数。
    因为多组数据之间map没有初始化各种RE TLE WA了20+次,最后又因为Yes写成了YES WA了几发,心累

  • 代码

/*
求树的最大独立集节点个数并判断最大独立集是否唯一
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
#define  LL  long long int
const int MAXN=2005;
map<string,int> mapp;
int n;
int index;
int tid[MAXN];//tid保存dfs序
int flag;//flag==1表示YES,最大独立集不唯一
int dp[MAXN][2];
int f[MAXN][2];//值为1表示有多种方案
int fa[MAXN];
struct Edge
{
    int v;
    int next;
}edge[MAXN*2];
int edgecount;
int head[MAXN];
void Init()
{
    memset(head,-1,sizeof(head));
    edgecount=0;
    index=0;
    flag=0;
    memset(fa,0,sizeof(fa));
    memset(dp,0,sizeof(dp));
    memset(f,0,sizeof(f));
    memset(tid,0,sizeof(tid));
}
void Add_edge(int u,int v)
{
     edge[++edgecount].v=v;
     edge[edgecount].next=head[u];
     head[u]=edgecount;
}
void In()
{
    mapp.clear();
    int number=0;
    string s;
    string a,b;
    cin>>s;
    mapp[s]=++number;
    for(int i=1;i<n;i++)
    {
        cin>>a>>b;//b是a的boss
        if(mapp.count(a)==0)mapp[a]=++number;
        if(mapp.count(b)==0)mapp[b]=++number;
        Add_edge(mapp[b],mapp[a]);
       // cout<<"edge: "<<mapp[b]<<" "<<mapp[a]<<endl;
    }
}
void Dfs(int u)
{
    tid[++index]=u;
    for(int k=head[u];k!=-1;k=edge[k].next)
    {
        int v=edge[k].v;
        if(v==fa[u])continue;
        fa[v]=u;
        Dfs(v);
    }
}
void Dp()
{
    for(int i=n;i>=1;i--)
    {
        int u=tid[i];
        dp[u][1]=1;
        dp[u][0]=0;
        for(int k=head[u];k!=-1;k=edge[k].next)
        {
            int v=edge[k].v;
            dp[u][1]+=dp[v][0];
            dp[u][0]+=max(dp[v][0],dp[v][1]);
            if(dp[v][0] > dp[v][1] && f[v][0]==1 ) f[u][0]=1;
            else if(dp[v][1] > dp[v][0] && f[v][1]==1)f[u][0]=1;
            else if(dp[v][0]==dp[v][1])f[u][0]=1;
            if(f[v][0]==1)f[u][1]=1;
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        Init();
        In();
        Dfs(1);
        Dp();
        printf("%d ",max(dp[1][0],dp[1][1]));
        if(dp[1][0]>dp[1][1]  && f[1][0]==1)printf("No\n");
        else if(dp[1][1]>dp[1][0]  && f[1][1]==1)printf("No\n");
        else if(dp[1][1]==dp[1][0])printf("No\n");
        else printf("Yes\n");//唯一
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ - 3616是一个题目,题目描述如下: 给定一组区间,每个区间有一个权重,要求选择一些区间,使得这些区间的右端点都小于等于k,并且权重之和最大。请问最大的权重和是多少? 解决这个问题的思路是使用动态规划。首先,将区间按照左端点从小到大进行排序。然后,定义一个dp数组,dp[i]表示右端点小于等于i的所有区间所能得到的最大权重。 接下来,遍历每一个区间,对于每个区间i,将dp[i]初始化为区间i的权重。然后,再遍历i之前的每个区间j,如果区间j的右端点小于等于k,并且区间j的权重加上区间i的权重大于dp[i],则更新dp[i]为dp[j]加上区间i的权重。 最后,遍历整个dp数组,找到最大的权重和,即为所求的答案。 下面是具体的代码实现: ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct interval{ int start, end, weight; }; interval intervals[10005]; int dp[10005]; int n, m, k; bool compare(interval a, interval b) { if (a.start == b.start) { return a.end < b.end; } else { return a.start < b.start; } } int main() { while(~scanf("%d %d %d", &n, &m, &k)) { memset(dp, 0, sizeof dp); for (int i = 0; i < m; i++) { scanf("%d %d %d", &intervals[i].start, &intervals[i].end, &intervals[i].weight); } sort(intervals, intervals + m, compare); for (int i = 0; i < m; i++) { dp[i] = intervals[i].weight; for (int j = 0; j < i; j++) { if (intervals[j].end <= k && dp[j] + intervals[i].weight > dp[i]) { dp[i] = dp[j] + intervals[i].weight; } } } int maxWeight = 0; for (int i = 0; i < m; i++) { maxWeight = max(maxWeight, dp[i]); } printf("%d\n", maxWeight); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值