HDU 4705 树形DP

Problem Description
 

Sample Input
  
  
4 1 2 1 3 1 4
 

Sample Output
  
  
1
Hint
1. The only set is {2,3,4}. 2. Please use #pragma comment(linker, "/STACK:16777216")
 
题意: 有一颗树, 选出3个点。 不在同一条路径上的集合数。
思路: DFS
间接法。
总的方案 - 在同一路径的。
从叶子节点开始。 
假设以i为节点子树的数量分别为 sum1, sum2, sumk;(i必选)
那么对每子树选一个点。 其他子树选一个点。有  sumi  * (n - 1 - sumi)
然后对i的子树选一个点,i树以外选一个点。
结果会重复算了一次。 所以要除上2
dfs时用的是 树形DP!

法一: 用 vector 存储图(树)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std;
__int64 ans;
int n;
bool vis[100005];
vector<int>vec[100005];

int dfs(int index) {
    int sum = 0;  //所有子树点的个数
    vis[index] = true;
    for(int i = 0; i < vec[index].size(); ++i)
        if(!vis[vec[index][i]]) {
            int temp = dfs(vec[index][i]);  // 某棵子树的个数
            sum += temp;
            ans += (__int64) temp * (n - 1 - temp);
        //    printf("%d %d:  %d %d %I64d\n",index,vec[index][i], temp, sum, ans);
        }
    if(sum)
        ans += (__int64) sum * (n - 1 - sum);
    sum++;
    return sum;
}

int main()
{
    int a,b,i,j;

    while(~scanf("%d",&n))
    {
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            vec[a].push_back(b);
            vec[b].push_back(a);
        }

        ans=0;
        memset(vis,0,sizeof(vis));
        dfs(1);
        __int64 tot=(__int64) n*(n-1)*(n-2)/6;  //此处要用强制转换,不然会越界!! 3WR
        printf("%I64d\n",tot-ans/2);
        for(i=1;i<=n;i++)
            vec[i].clear();
    }

    return 0;
}

法二: 用链式前向星 存储图
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:16777216")
#define V 100050
using namespace std;

struct Node
{
    int pre,v;
}e[V*2];

int p[V*2],num;
bool vis[V*2];
__int64 ans,n;

void addedge(int u,int v)
{
    e[num].v=v;
    e[num].pre=p[u];
    p[u]=num++;
}

int dfs(int index)
{
    int i,j,sum=0;
    vis[index]=1;
    for(j=p[index]; j!=-1; j=e[j].pre)
    {
        int v=e[j].v;
        if(vis[v]) continue;
        int tmp=dfs(v);
        sum+=tmp;
        ans+= (__int64)tmp*(n-1-tmp);
    }

    if(sum) ans+=sum*(n-1-sum);
    sum++;
    return sum;
}


int main()
{
    int i,j,a,b;

    while(~scanf("%I64d",&n))
    {
        memset(p,-1,sizeof(p));
        memset(vis,0,sizeof(vis));
        num=0;
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }

      /*   for(i=1;i<=n;i++)
            {
                printf("%d: ",i);
                for(j=p[i];j!=-1;j=e[j].pre)
                    printf("%d ",e[j].v);
                printf("\n");
            }  */
        ans=0;
        dfs(1);
        __int64 tot= n*(n-1)*(n-2)/6;
        printf("%I64d\n",tot-ans/2);
    }


    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值