LightOJ 1252 Maintaining Communities(树形)

本文介绍了一种使用动态规划(DP)方法解决树的划分问题的算法。问题的核心在于将一棵树划分成若干块,使得每块的权值之和不超过给定阈值m,同时求出最少的划分块数。通过定义状态f[u][id][c][flag],文章详细阐述了状态转移方程,并提供了完整的C++代码实现。

题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1252

题意:给出一棵树,将其分成若干块,使得每块的权值之和均不大于m。求最少需要分成几块?

思路:f[u][id][c][flag]表示当前计算以u的第id个孩子为根的子树,flag表示u与u的父节点的关系:flag=1表示u是独立的一块,

flag=0表示u与u的父节点是一块,c表示u所在块内当前的权值。设u的第id个孩子为v,<u,v>=w。则转移为:

(1)若c+w<=m说明v可以与u在一个块上,则递归f[v][0][c+w][0]和f[u][id+1][len][flag](len为f[v][0][c+w][0]返回的权值);当然此时也可以选择v不与u在一个块上,则递归f[v][0][0][1]和f[u][id+1][c][flag];

(2)若c+w>m,则只能选择递归f[v][0][0][1]和f[u][id+1][c][flag]。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

struct node
{
    int v,w,next;

    node(){}
    node(int _v,int _w)
    {
        v=_v;
        w=_w;
    }
};

struct Node
{
    int cnt,len;

    Node(){}
    Node(int _cnt,int _len)
    {
        cnt=_cnt;
        len=_len;
    }
};

const int INF=1000000000;
const int MAX=105;
int C,num=0;
vector<node> G[MAX];
node edges[MAX*2];
int head[MAX],e;
int n,m;
Node f[MAX][MAX][MAX][2];
int visit[MAX][MAX][MAX][2];


void Add(int u,int v,int w)
{
    edges[e].v=v;
    edges[e].w=w;
    edges[e].next=head[u];
    head[u]=e++;
}

void DFS1(int u,int pre)
{
    int i,v,w;
    for(i=head[u];i!=-1;i=edges[i].next)
    {
        v=edges[i].v;
        w=edges[i].w;
        if(v==pre) continue;
        DFS1(v,u);
        G[u].push_back(node(v,w));
    }
}

Node DFS(int u,int id,int c,int flag)
{
    if(id==G[u].size()) return Node(flag,c);
    if(visit[u][id][c][flag]) return f[u][id][c][flag];
    visit[u][id][c][flag]=1;
    Node t1,t2,ans=Node(INF,INF);
    int v,w;
    v=G[u][id].v;
    w=G[u][id].w;
    if(c+w<=m)
    {
        t1=DFS(v,0,c+w,0);
        t2=DFS(u,id+1,t1.len,flag);
        ans=Node(t1.cnt+t2.cnt,t2.len);
    }
    t1=DFS(v,0,0,1);
    t2=DFS(u,id+1,c,flag);
    t2.cnt+=t1.cnt;

    if(t2.cnt<ans.cnt||t2.cnt==ans.cnt&&t2.len<ans.len) ans=t2;
    return f[u][id][c][flag]=ans;
}

int main()
{
    for(scanf("%d",&C);C--;)
    {
        scanf("%d%d",&n,&m);
        int i,u,v,w;
        for(i=1;i<=n;i++) G[i].clear(),head[i]=-1;
        e=0;
        for(i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            Add(u,v,w);
            Add(v,u,w);
        }
        DFS1(1,-1);
        memset(visit,0,sizeof(visit));
        Node ans=DFS(1,0,0,1);
        printf("Case %d: %d\n",++num,ans.cnt);
    }
}

  

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值