1. 程式人生 > >2017.10.27

2017.10.27

pre 最大值 對他 ostream {} 因特網 clas fin 紀念

今天主要復習背包內容了,比較基礎的dp,好久不打仍然會手生。其實復習考了這麽多次,發現我們其實考試的大部分內容都是學過的,但是經常會因為不常打造成“這個我學過,但寫不出來”的尷尬局面。所以經常復習,刷刷版子還是很重要的。

還有幾道水的就不寫了,放幾道今天稍微要想一下的

1.金明的預算方案


題目描述

金明今天很開心,家裏購置的新房就要領鑰匙了,新房裏有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎麽布置,你說了算,只要不超過N元錢就行”。今天一早,金明就開始做預算了,他把想買的物品分為兩類:主件與附件,附件是從屬於某個主件的,下表就是一些主件與附件的例子:

主件 附件

電腦 打印機,掃描儀

書櫃 圖書

書桌 臺燈,文具

工作椅 無

如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有0個、1個或2個附件。附件不再有從屬於自己的附件。金明想買的東西很多,肯定會超過媽媽限定的N元。於是,他把每件物品規定了一個重要度,分為5等:用整數1~5表示,第5等最重要。他還從因特網上查到了每件物品的價格(都是10元的整數倍)。他希望在不超過N元(可以等於N元)的前提下,使每件物品的價格與重要度的乘積的總和最大。

設第j件物品的價格為v[j],重要度為w[j],共選中了k件物品,編號依次為j1,j2,……,jk,則所求的總和為:

v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*為乘號)

請你幫助金明設計一個滿足要求的購物單。

輸入輸出格式

輸入格式:

輸入的第1行,為兩個正整數,用一個空格隔開:

N m (其中N(<32000)表示總錢數,m(<60)為希望購買物品的個數。)

從第2行到第m+1行,第j行給出了編號為j-1的物品的基本數據,每行有3個非負整數

v p q (其中v表示該物品的價格(v<10000),p表示該物品的重要度(1~5),q表示該物品是主件還是附件。如果q=0,表示該物品為主件,如果q>0,表示該物品為附件,q是所屬主件的編號)

輸出格式:

輸出只有一個正整數,為不超過總錢數的物品的價格與重要度乘積的總和的最大值(<200000)。

輸入輸出樣例

輸入樣例#1:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
輸出樣例#1:
2200


題解:對於每一個主件,把它以及它搭配各附件的情況分別列舉,轉換成分組背包。
因為價格是10的倍數,除10後處理,就會優化時空復雜度。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,v,p,q,dp[30010],ans,k,mp[30010];

struct node{
    int v,w;
    node(int x,int y):v(x),w(y){}
    node(){}
};
vector<node> a[65];
int main(){
    scanf("%d%d",&n,&m);
    n/=10;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&v,&p,&q);
        v/=10;
        if(q==0){
            a[++k].push_back(node(v,v*p));
            mp[i]=k;
        }
        else{
            int len=a[mp[q]].size();
            for(int j=0;j<len;j++){
                a[mp[q]].push_back(node(v+a[mp[q]][j].v,a[mp[q]][j].w+v*p)); 
            }
        }
    }
    for(int i=1;i<=k;i++){
        for(int j=n;j>=0;j--)
            for(int k=0;k<(a[i].size());k++){
                if(j>=a[i][k].v)dp[j]=max(dp[j],dp[j-a[i][k].v]+a[i][k].w);
            }
    }
    printf("%d\n",dp[n]*10);
    return 0;
}

2.瘋狂的采藥


題目背景

此題為NOIP2005普及組第三題的瘋狂版。

此題為紀念LiYuxiang而生。

題目描述

LiYuxiang是個天資聰穎的孩子,他的夢想是成為世界上最偉大的醫師。為此,他想拜附近最有威望的醫師為師。醫師為了判斷他的資質,給他出了一個難題。醫師把他帶到一個到處都是草藥的山洞裏對他說:“孩子,這個山洞裏有一些不同種類的草藥,采每一種都需要一些時間,每一種也有它自身的價值。我會給你一段時間,在這段時間裏,你可以采到一些草藥。如果你是一個聰明的孩子,你應該可以讓采到的草藥的總價值最大。”

如果你是LiYuxiang,你能完成這個任務嗎?

此題和原題的不同點:

1.每種草藥可以無限制地瘋狂采摘。

2.藥的種類眼花繚亂,采藥時間好長好長啊!師傅等得菊花都謝了!

輸入輸出格式

輸入格式:

輸入第一行有兩個整數T(1 <= T <= 100000)和M(1 <= M <= 10000),用一個空格隔開,T代表總共能夠用來采藥的時間,M代表山洞裏的草藥的數目。接下來的M行每行包括兩個在1到10000之間(包括1和10000)的整數,分別表示采摘某種草藥的時間和這種草藥的價值。

輸出格式:

輸出一行,這一行只包含一個整數,表示在規定的時間內,可以采到的草藥的最大總價值。

輸入輸出樣例

輸入樣例#1:
70 3
71 100
69 1
1 2
輸出樣例#1:
140

說明

對於30%的數據,M <= 1000;

對於全部的數據,M <= 10000,且M*T<10000000(別數了,7個0)。

題解:完全背包,挺裸的。就是把01背包正過來做。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 10000010
using namespace std;
int t[maxn],v[maxn],f[maxn];
int main()
{
    int m,n;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
       scanf("%d%d",&t[i],&v[i]);
    for(int i=1;i<=n;i++)
      for(int j=t[i];j<=m;j++)
        f[j]=max(f[j],f[j-t[i]]+v[i]);
    printf("%d\n",f[m]);
    return 0;
}

3.二叉蘋果樹


題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有只有1個兒子的結點)

這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。

我們用一根樹枝兩端連接的結點的編號來描述一根樹枝的位置。下面是一顆有4個樹枝的樹

2 5 \ / 3 4 \ / 1 現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。

給定需要保留的樹枝數量,求出最多能留住多少蘋果。

輸入輸出格式

輸入格式:

第1行2個數,N和Q(1<=Q<= N,1<N<=100)。

N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的信息。

每行3個整數,前兩個是它連接的結點的編號。第3個數是這根樹枝上蘋果的數量。

每根樹枝上的蘋果不超過30000個。

輸出格式:

一個數,最多能留住的蘋果的數量。

輸入輸出樣例

輸入樣例#1:
5 2
1 3 1
1 4 10
2 3 20
3 5 20
輸出樣例#1:
21

題解:樹形dp,算比較難的一類dp吧,不過這道題還不是很難。
dp[i][j]表示以i為根保留j條的最優解。要先建樹,然後dfs。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[2010][2010],w,head[2010],fa[2010],tot,n,tt,sum,cnt,q,u,v;
struct edge{
    int next,v,w;
}E[2010];
void add(int u,int v,int w){
    E[++cnt].v=v;
    E[cnt].w=w;
    E[cnt].next=head[u];
    head[u]=cnt;
}
int dfs(int x){
    int ss=0;
    for(int i=head[x];i;i=E[i].next){
        int v=E[i].v,w=E[i].w;
        if(v==fa[x])continue;
        fa[v]=x;
        ss+=dfs(v)+1;
        for(int j=min(q,ss);j>=1;j--)
            for(int k=min(q,j);k>=1;k--)
                dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[v][k-1]+w);
    }
    return ss;
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    dfs(1);
    printf("%d",dp[1][q]);
    return 0;
}

2017.10.27