문제에 대한 해결 방법 [P1064] JINMING 예산 계획

주제 링크

솔루션 JINMING 예산 계획 [NOIP 그룹 2006 년 개선]

타이틀 효과 : 일련의 항목 지정된 각 제품의 가격 갖는다 \ (V의 \) 및 중량 \ (W \) 실행 가능한 형태의 문서를 검색하고, 의존성 등 의존성을 만족하도록. \ (\ 합 V \의 당량의 m의 \) 의 경우 \ (\ \) w 합계 최대한 크게

분석 : 항목 경우이 질문은 분명, 예를 들어, 종속성 트리 있음을 알려 (V \) \ 항목을 의존 \ (U \) , 우리조차하지 않는 측면 \ (U, V \) 의 편의를 위해, 우리는. 가상 포인트 가입 \ (0 \) , 점 \을 (U \) 우리가 심지어 중요한 것은 경우 \ ((0, U) \ )

그래서 우리는이 나무가 어떤 특성을 가지고, 나무와 끝까지?

  • 모든 항목이 자신의 부모 있습니다에 항목은 의존
  • 항목을 선택하지 않으면 나무에 해당하고, 그의 모든 자식 노드는 선택할 수 없습니다

음, 나무는 첫째, 우리가 사용할 수 있습니다.의 문제를 해결하는 방법을 살펴 보자. 밖으로 구축 트리 DP 해결하기를, 그러나 이것은 더 복잡하지만 가능한 더 잘 알고 사용하는 그래서. 매우 빠르게 실행되지 \ 01 \ ( ) 배낭 을 해결하기 위해?

일반 \ (01 \) 배낭 및 전송 상태 방정식?

\ [I] [J] = 최대 \ f를 {케이스} 시작 (F [I - 1] [J] \\ F [I - 1] [J - V] + w & J \의 GEQ 브이 \ 단부 {예} \)

나는 우리가 아주 잘 알고있는 생각이 문제를 봐 나무 \ (DP \)가 , 잘 할 우리으로 바꿀 수있는 선형 (DP) . 우리가 할 수있는 방법의 순서에 나무를 넣어? \합니다 (DFS \) 순서 아

우리는 나중에 Fengyun의 트리 탐색 순서를 찾아 우리가이 결정을 무슨? 모든 단계에서 다음과 같은 노드 아들과 형제는 앞의 순서로 남아있다. 그리고.

  • 이 항목을 다음 상태 선택 \ (나는 \)를 주하여 \ (전 - 1 \) 로부터 전송
  • 아니이 기사에 대한 투표, 그렇지 선택할 수 없습니다 포인트의 아들 , 그 상태 \ (나는 \) 에만 왼쪽에서 자사의 형제 전송할 수 있습니다

경우 깔끔함, \는 (프리 [U]는 \) 그 수는 \ (U \) 단어의 좌측 형제 번호 :

\ [I] [J] = 최대 \은 [이전 [I] [J] \\ F [I - 1 f는 {예를} 시작 (F [J - V] + w & J \ GEQ 브이 \ 단부 {예 } \)

시간 복잡도를 분석? postorder 및 탐색 \ (\ 사전) 우리가 한 번에 할 수 \ DFS (\) 에서 (O (N) \) \ 시간에 알아

  • 상태의 전체 개수 : \ (㎚ \)
  • 전송 비용 : \ (O (1) \)
    ? 그럼 총 복잡도 \ (O (N + ㎚) \) 때문에 전체적인 복잡성 \ (O (㎚) \)\ (01 \) 상당한 배낭이 알고리즘은 또한 첨부 파일 첨부 (즉, 트리의 높이보다 더 처리 할 수있다 \ (2 \) 경우)

추한 코드를 연결합니다 :

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 64;
const int maxm = maxn;
const int maxv = 33333;
struct Node{//表示物品
    int v,w;
    Node() = default;
    Node(int a,int b):v(a),w(b){}
}d[maxn],t[maxn];
struct Edge{
    int from,to;
    Edge() = default;
    Edge(int a,int b):from(a),to(b){}
}Edges[maxm];
int head[maxn],nxt[maxm],root[maxn],rp;
inline void addedge(int from,int to){
    static int tot;
    Edges[++tot] = Edge(from,to);
    nxt[tot] = head[from];
    head[from] = tot;
}
int pre[maxn],f[maxn][maxv],p = 0;
void dfs(int u){
    int tp = p;//由于后序遍历的性质,一个点的左兄弟显然是进入这个点时序列中的最后一个点
    for(int i = head[u];i;i = nxt[i]){
        Edge &e = Edges[i];
        dfs(e.to);
    }
    d[++p] = t[u];//后序遍历
    pre[p] = tp;//求左兄弟,注意,pre[t]表示序列中编号为t的节点的左兄弟的编号
}
int n,m;
int main(){
#ifdef LOCAL
    freopen("fafa.in","r",stdin);
#endif
    scanf("%d %d",&m,&n);
    for(int i = 1;i <= n;i++){//建图
        int v,w,faz;
        scanf("%d %d %d",&v,&w,&faz);
        t[i] = Node(v,v * w);//先预处理它的权值
        addedge(faz,i);//有个技巧,如果一个点是主件,我们就认为它依赖于虚拟点0
    }
    dfs(0);
    for(int i = 1;i <= n;i++)//dp求解
        for(int j = 0;j <= m;j++)
            if(j >= d[i].v)f[i][j] = max(f[pre[i]][j],f[i - 1][j - d[i].v] + d[i].w);
            else f[i][j] = f[pre[i]][j];//转移 
    printf("%d\n",f[n][m]);
    return 0;
}

추천

출처www.cnblogs.com/colazcy/p/11514778.html