2014 UESTC Training for Dynamic Programming L

树形DP,除了求出最少需要的次数还需要求出最小次数对应的方案数
dp[u][1]表示u这个点改变为白色,且u这个子树满足条件的最小次数
dp[u][0]表示u这个点不改变且u这棵子树满足条件时的最少次数
转移就是dp[u][1]=
sum(min(dp[vi][1],dp[vi][0]) )+1;
dp[u][0]=sum(dp[vi][1]); 
(v是u的全部子节点)
另外再开一个num数组跟着一起转移就可以
注意转移是要用乘法不是加法,每次叶子节点初始化为num[u][0]=num[u][1]=1;

而且注意最后如果dp[1][0]==dp[1][1].那么ans2=num[1][0]+num[1][1]也要再次取模。这里仍然存在加抄10007的可能性

#include <map>
#include <set>
#include <list>
#include <cmath>
#include<cctype>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b)
{
	return a % b == 0 ? b : gcd(b, a % b);
}
#define MAXN 100005
#define MOD 10007
int dp[MAXN][2];
int num[MAXN][2];
int ans1;LL ans2;
vector <int>vv[MAXN];
int N;
void add(int u,int v)
{
    vv[u].push_back(v);
}
void init()
{
    for (int i=0;i<=N;i++)
        vv[i].clear();
}
void dfs(int u,int fa)
{
    dp[u][0]=0;dp[u][1]=1;
    num[u][0]=1;num[u][1]=1;
    for (int i=0;i<vv[u].size();i++)
    {
        int v=vv[u][i];
        if (v==fa) continue;
        dfs(v,u);
        dp[u][0]+=dp[v][1];
        num[u][0]*=num[v][1];
        num[u][0]%=10007;
        if(dp[v][0]<dp[v][1])
        {
          dp[u][1]+=dp[v][0];
          num[u][1]*=num[v][0];
        }
        else if(dp[v][0]>dp[v][1])
        {
          dp[u][1]+=dp[v][1];
          num[u][1]*=num[v][1];
        }
        else
        {
          dp[u][1]+=dp[v][1];
          num[u][1]*=(num[v][0]+num[v][1]);
        }
         num[u][1]%=10007;
    }
}
void slove()
{
    scanf("%d",&N);
    init();
    int x,y;
    for (int i=1;i<N;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,-1);
    ans1=min(dp[1][0],dp[1][1]);
    if (dp[1][0]==dp[1][1]) ans2=num[1][0]+num[1][1];
    else if (dp[1][0]>dp[1][1]) ans2=num[1][1];
    else ans2=num[1][0];
   // for (int i=1;i<N;i++) printf("dp[%d][1]=%d   ,dp[%d][0]=%d\n",i,dp[i][1],i,dp[i][0]);
   // for (int i=1;i<=N;i++) printf("nun[%d][1]=%d   ,num[%d][0]=%d\n",i,num[i][1],i,num[i][0]);
    printf("%d %lld\n",ans1,ans2%10007);
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    slove();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值