POJ 3345 Bribing FIPA

本文深入探讨了BFS与DFS在解决树形动态规划问题时的应用,详细解释了如何通过树形DP解决不考虑树根的情况下选择j个点所需的最小花费问题,并提供了具体的代码实现。

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

Bribing FIPA

Tree DP。由于选定某棵子树的根之后,所有的子节点都被选择。我们先处理出不考虑树根的情况,选择j个点所需要的最小花费。然后对于每个j进行判断,如果对应的价值大于树根的价值,就更新。直接看代码:

/*
  *author    : csuchenan
  *PROG      : POJ3345
  *Algorithm : Tree DP dp[i][j]表示以i为根节点选取j个点所有需要的最小代价
  *notice    : 读入时需要注意,用STL的会比较好写一点,对于dp[i][j]的值计算的时候
  *            需要注意
  *csuchenan	3345	Accepted	416K	16MS	C++	2416B
*/
#include <cstdio>
#include <cstring>
#include <vector>
#include <sstream>
#include <iostream>
using std::stringstream ;
using std::vector ;
using std::cin ;

#define maxn 205
#define INF 0x3f3f3f3f

int  val[maxn] ;
int  num[maxn] ;
char name[maxn][105] ;
int  dp[maxn][maxn]  ;
vector<int> G[maxn]  ;
int n , m ;
int len ;

int insert(char * str){
    len ++ ;
    strcpy(name[len] , str) ;
    return len ;
}
int find(char * str){
    int i = 0;
    for( ; i <= len && strcmp(str , name[i])!=0 ; i ++)  ;
    return i > len ? -1 : i ;
}

void init(){
    for(int i = 0 ; i <= n ; i ++){
        G[i].clear() ;
    }
    memset(dp , 0x3f , sizeof(dp)) ;
    memset(num , 0  , sizeof(num)) ;
    len = 0 ;
}

bool read(){

    char str[1005] ;
    char tmp[105]  ;

    cin.getline(str , sizeof(str)) ;

    if(str[0] == '#')
        return 0 ;

    int p , q ;
    int cnt   ;

    sscanf(str , "%d%d" , &n , &m) ;
    init() ;

    for(int i = 0 ; i < n ; i ++){
        scanf("%s%d" , tmp , &p) ;
        if( (q = find(tmp)) == -1 )
            q = insert(tmp) ;
        val[q] = p ;

        cin.getline(str , sizeof(str)) ;
        stringstream ss(str) ;
        while(ss>>tmp){
            if((p = find(tmp))==-1)
                p = insert(tmp) ;
            G[q].push_back(p) ;
            num[p] ++ ;
        }
    }
    return 1 ;
}
int dfs(int v){
    int cnt = 1 ;
    for(int i = 0 ; i != G[v].size() ; i ++){
        int u = G[v][i] ;
        cnt += dfs(u) ;
    }
    dp[v][0] = 0 ;
    for(int i = 0 ; i != G[v].size() ; i ++){
        int u = G[v][i] ;
        for(int j = cnt ; j >= 0 ; j --){
            for(int k = 0 ; k <= j ; k ++){
                if(dp[v][j] > dp[v][j - k] + dp[u][k]){
                    dp[v][j] = dp[v][j - k] + dp[u][k] ;
                }
            }
        }
    }
    for(int i = 0 ; i <= cnt ; i ++){
        dp[v][i] = (dp[v][i] > val[v]) ? val[v] : dp[v][i] ;
    }
    return cnt ;
}
void solve(){
    val[0] = INF ;

    for(int i = 1 ; i <= n ; i ++){
        if(num[i] == 0)
            G[0].push_back(i) ;
    }

    dfs(0) ;
    int ans = INF ;

    for(int i = m ; i <= n ; i ++){
        ans = ans < dp[0][i] ? ans : dp[0][i] ;
    }
    printf("%d\n" , ans) ;
}
int main(){
    while(read()){
        solve() ;
    }
    return 0 ;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值