Color(树形dp)——2017 Wuhan University Programming Contest (Online Round)

本文介绍了一道关于树形结构的染色问题,并通过树形动态规划的方法求解。问题设定为在一个由N个点构成的树上,每个点可以染成M种颜色中的一种,且相邻点不能染成相同颜色,求所有合法染色方案的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2017 Wuhan University Programming Contest (Online Round)
B.Color

Input file: standard input
Output file: standard output
Time limit: 1 second
Memory limit: 512 mebibytes

When Asuho was just a little girl, she has been loving stars, as there are so many romantic stories about old legends and the stars.
Today, as usual, Asuho went to the observatory with her beloved boyfriend, Kogasaka Ming, to see the splendid sky of galaxy. Sitting with Kogasaka shoulder to shoulder, Asuho thought nothing could make her happier.
“Asuho,” Kogasaka called Asuho softly. “I have a question about the Hoshizora(night sky with stars).”
“What’s it?” Asuho answered, happily. “I know everything about the stories, you can ask anything.”
Kogasaka smiled, “You see the stars? Just imagine there are n distinct stars in the sky while n−1 relationships connect them. Each relationship is between two stars and all stars are connected directly or indirectly. Now you need to divide the
n stars into m different types. Beware that no two directly connected stars should share the same type and some stars cannot be some types. I want you to tell me how many distinct solutions there could be.”
Suddenly the Stella meteor shower appeared, so the lovers forgot the question and started to admire the beautiful Hoshizora.
Asuho thought it was unbelievable that Kogasaka should ask her such a foolish question which she can’t solve. Of course she didn’t wish to reveal her poor math. So can you help her to tell the answer?
Input
The Input file contains several test cases.
For each case, the first line contains two integers n(2≤n≤10^4​​), m(1≤m≤20)
The next n−1 lines, each line contains 2 integers xi​​, y​i​​ expressing relationships between the two stars.
The next n lines, each lines contains m numbers and each of the m is 0 or 1.
The i-th number of the j-th line respect whether j-th star can be divided into the i-th type. 1 means it could while 0 means not.
Output
For each test case, output one single line, containing the number of solutions module 10^7+9
Input
4 6
1 2
1 3
1 4
1 1 1 1 1 0
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
Output
625
题意:有N个点,其间有N-1条边使其连通,有M种颜色,给这些点涂色,各点可选用的颜色如输入的0-1矩阵所示,相邻的两个点不能涂同一种颜色,问有多少种涂色方案。

虽然发现了这题的树形结构,却一点也没想到要利用这个性质……
做的第一道树形dp,感觉还是很巧妙的。

dp[i][j]表示的是以i为根的子树在i涂色为j时的方案数量。
首先要把输入的图转化为以1为根的数,见buildTree函数。
*当i为叶子节点时,第一层dp[i][j]存入的就是c[i][j]的值。
*当i不是叶子节点时,先处理其叶子节点,保证在处理该节点时,其所有叶子节点都已处理完毕,就可以继承叶子节点的数据。
处理方法是:
1)c[i][j]=0,dp[i][j]=0
2)c[i][j]=1,dp[i][j]=Π(Σdp[i的子节点][k!=j])
最后Σdp[1][i]的值就是答案。

感觉dp的关键就是,找到一个数据的继承方向,具体继承的量,该量的存储方式。
在这里,方向就是沿叶子节点向根节点,自下向上继承方案数,分颜色存储数据,因为颜色的情况是计算新方案数的必要条件。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 10005
vector<int>G[maxn];//存入图
vector<int>son[maxn];//以父子关系建立有根数
int dp[maxn][25];//以i为根的子树,在i染色为j时的方案数
bool c[maxn][25];//可染色的情况
bool vis[maxn];
int N,M;
void init(){
    int i;
    memset(dp,0,sizeof(dp));
    memset(vis,false,sizeof(false));
    for(i=1;i<=N;i++){
        G[i].clear();
        son[i].clear();
    }
}
void buildTree(int root){
    //转化为以1为根的树
    int i,term;
    if(vis[root])
        return;
    vis[root]=true;
    for(i=0;i<G[root].size();i++){
        term=G[root][i];
        if(!vis[term]){
            son[root].push_back(term);
            buildTree(term);
        }
    }
}
int dfs(int root){
    int i,j,k,S;
    long long res,sum;
    if(son[root].size()==0){//节点是叶子
        for(i=1;i<=M;i++){
            if(c[root][i])
                dp[root][i]=1;
            else dp[root][i]=0;
        }
        return 0;
    }
    for(i=0;i<son[root].size();i++){//转向其子树
        dfs(son[root][i]);
    }
    for(i=1;i<=M;i++){
        if(c[root][i])
            sum=1;
        else{
            dp[root][i]=0;
            continue;
        }
        for(j=0;j<son[root].size();j++){
            S=son[root][j];
            res=0;
           for(k=1;k<=M;k++){
               if(k!=i)
                   res=(res+dp[S][k])%10000009;
           }
           sum=(sum*res)%10000009;
        }
        dp[root][i]=sum;
        //printf("%d\n",sum);
    }
    return 0;
}
int main(){
    int i,j,k;
    int f,s;
    while(scanf("%d %d",&N,&M)!=EOF){
        init();
        for(i=1;i<N;i++){
            scanf("%d %d",&f,&s);
            G[f].push_back(s);
            G[s].push_back(f);
        }
        for(i=1;i<=N;i++){
            for(j=1;j<=M;j++){
                scanf("%d",&c[i][j]);
            }
        }
        buildTree(1);
        dfs(1);
        long long counts=0;
        for(i=1;i<=M;i++){
            counts=(counts+dp[1][i])%10000009;
        }
        printf("%I64d\n",counts);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值