poj1947 Rebuilding Roads 树形dp 背包

poj1947 Rebuilding Roads
题目大意:求树上隔离出k个点的最少删除的边数。n,k<=150 空间限制30000k
题解:
(看了网上的题解大雾了一天TAT)
(但是当你一旦弄明白后发现卧槽他们说的好对啊哭晕QAQ)
首先这样想 dp[i,j] 表示以i为根节点的子树剩余j个节点删除的最多的边数 这样 相当于分组背包(当时一直不明白QAQ蠢) dp[i,j]有son[i] 个分组,每次都从以i为根的物品组中选择一个物品进行转移,每组选择一个物品。
和01背包类似 分组背包也有二维和一维两组写法(这里是三维和二维)
三维MLE 二维正解……
UP:背包问题值得好好研究一番……
背包九讲
贴代码:三维MLE

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200;
const int inf=0x7fffffff;
struct E {int to,nxt;}edge[N*2];
int idx[N],tot=1,cnt[N];
int n,k,ans=inf,root;
int dp[N][N][N],son[N];
bool vis[N];
void addedge(int from,int to){
    edge[tot].to=to;edge[tot].nxt=idx[from];idx[from]=tot++;
}
void dfs(int x,int fa){
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(e.to!=fa){
            dfs(e.to,x);
            son[x]++;
        }
    }
    if(x==root) fill(dp[x][1],dp[x][1]+1+son[x],son[x]);
    else fill(dp[x][1],dp[x][1]+1+son[x],son[x]+1);
    int Cnt=0;
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(e.to!=fa){
            Cnt++;
            for(int i=2;i<=k;i++){
                dp[x][i][Cnt]=dp[x][i][Cnt-1];
                for(int j=1;j<i;j++)
                    dp[x][i][Cnt]=min(dp[x][i][Cnt],dp[x][i-j][Cnt-1]+dp[e.to][j][son[e.to]]-2);
            }
            //if(x==4) printf("%d<",dp[x][4][Cnt]);
        }
    }
    //printf("%d %d %d\n",x,5,dp[x][5][son[x]]);
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);cnt[y]++;
    }
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)for(int k=0;k<N;k++)dp[i][j][k]=1000000;
    for(int i=1;i<=n;i++) if(!cnt[i]) root=i,dfs(i,0);
    int ans=dp[root][k][son[root]];
    for(int i=1;i<=n;i++) if(i!=root) ans=min(ans,dp[i][k][son[i]]);
    printf("%d\n",ans);
    return 0;
}

二维正解

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200;
const int inf=0x7fffffff;
struct E {int to,nxt;}edge[N*2];
int idx[N],tot=1,cnt[N];
int n,k,ans=inf,root;
int dp[N][N],son[N];
bool vis[N];
void addedge(int from,int to){
    edge[tot].to=to;edge[tot].nxt=idx[from];idx[from]=tot++;
}
void dfs(int x,int fa){
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(e.to!=fa){
            dfs(e.to,x);
            son[x]++;
        }
    }
    if(x==root) dp[x][1]=son[x];
    else dp[x][1]=son[x]+1;
    for(int t=idx[x];t;t=edge[t].nxt){
        E e=edge[t];
        if(e.to!=fa){
            for(int i=k;i>=2;i--){
                for(int j=1;j<i;j++)
                    dp[x][i]=min(dp[x][i],dp[x][i-j]+dp[e.to][j]-2);
            }
            //if(x==4) printf("%d<",dp[x][4][Cnt]);
        }
    }
    //printf("%d %d %d\n",x,5,dp[x][5][son[x]]);
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);cnt[y]++;
    }
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)dp[i][j]=1000000;
    for(int i=1;i<=n;i++) if(!cnt[i]) root=i,dfs(i,0);
    int ans=dp[root][k];
    for(int i=1;i<=n;i++) if(i!=root) ans=min(ans,dp[i][k]);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(dp,poj,背包,树形DP)