18寒假12测

Posted Ed_Sheeran

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18寒假12测相关的知识,希望对你有一定的参考价值。

Day1

题目名称

party

array

treasure

输入

party.in

array.in

treasure.in

输出

party.out

array.out

treasure.out

每个测试点时限

1秒

1秒

1秒

内存限制

64MB

64MB

64MB

测试点数目

10

10

10

每个测试点分值

10

10

10

是否有部分分

题目类型

传统

传统

传统

 

 

party

题目描述:

在M公司里,每个人只有一个直属上司(除了boss)。这个公司举办派对,每个人可以给派对带来一定的欢乐值,但是每个人不能和自己的上司同时参加这个派对,求派对的最大欢乐值。

输入:

      第一行n表示公司有n个人。

第二行n个数,表示每个人的上司是谁,如果这个人的上司为0,说明这个人是boss。

      第三行n个数,表示每个人的欢乐值为wi。

输出:

一行一个数表示最大欢乐值。

样例输入:

6

0 1 1 1 4 4

1 1 1 1 1 1

样例输出:

4

解释:2 3 5 6 同时出席。

数据规模:

对于30%的数据,n<=20。

对于100%的数据,n<= 1000000,0<=w[i]<=1000。

题解:树形dp,dp[i][0/1]表示i号节点来不来,若父亲来,儿子肯定不来,若父亲不来,儿子随便来不来;

dp[i][0] = sum( max (dp[son[i]][0], dp[son[i]][1]); dp[i][1] = sum(dp[son[i]][0]);

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1000005;
int head[maxn],tot,dp[maxn][2];
struct edge{
    int to,nxt;
}G[maxn + 100];
void add(int u,int v){
    G[++tot].nxt = head[u];
    G[tot].to = v;
    head[u] = tot;
}
void dfs(int u, int f){
    
    for(int i = head[u]; i; i = G[i].nxt){
        int v = G[i].to;
        //if(v == f)continue;
        dfs(v, u);
        dp[u][1] += dp[v][0];
        dp[u][0] += max(dp[v][0], dp[v][1]);
    }
}
int main(){
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout);
    int n;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        int fa;
        scanf("%d",&fa);
        add(fa, i);
    }
    for(int i = 1; i <= n; i++){
        int w;
        scanf("%d",&w);
        dp[i][1] = w;
    }
    dfs(0, 0);
    cout<<dp[0][0]<<endl;
}

 

array

题目描述:

            给定两个长度为n的排列p1和p2,求它们的最长公共子序列。

输入:

            第一行一个数n。

接下来两个n个数分别表示p1和p2。

输出:

            一个数表示最长子序列的长度。

样例输入:

3

1 2 3

2 1 3

样例输出:

2

解释及说明:

共有两种方案,分别为子序列为13和子序列为23.

数据规模:

对于30%的数据,n<=2000.

对于100%的数据,n<=100000。

题解:由于是排列,所以上下元素一样,且一个序列中无重复元素,我们就把一个序列的元素顺序做一个映射,在第二个序列中作最长上升子序列

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1000005;
int d[maxn], mp[maxn], q[maxn], tail;
int main(){
    freopen("array.in","r",stdin);
    freopen("array.out","w",stdout);
    int n, a, b;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        scanf("%d",&a);
        mp[a] = i;
    }
    for(int j = 1; j <= n; j++){
        scanf("%d",&b);
        d[j] = mp[b];
    }
    for(int i = 1; i <= n; i++){
        if(d[i] > q[tail])q[++tail] = d[i];
        else {
            int pos = lower_bound(q + 1, q + 1 + tail, d[i]) - q;
            q[pos] = d[i];
        }
    }
    cout<<tail<<endl;
}
 

 

treasure

题目描述:

小a可以攻打m座城堡,攻打每个城堡有不同的宝物,但是在攻打有些城堡前,需要攻打另外的城堡。小a想知道,能获得的最多宝物是多少?

输入:

第一行两个数,n,m。分别表示有n个城堡,可以攻打m个。

接下来n行每行两个数a,b,分别表示在攻打第i个城堡前需要攻打a,攻打后获得b的宝物。

输出:

一行一个数ans表示可以获得的最大宝物数量。

样例输入

3 2

0 1

0 2

0 3

7 4

2 2

0 1

0 4

2 1

7 1

7 6

2 2

0 0

样例输出:

5

13

 

数据规模:

            对于30%的数据,n<=20,m<=20。

对于100%的数据,n<=500,m<=500,b[i]<=1000。

题解:树形DP,这道题很像树上染色,但不用考虑子树和外界 联系,要简单一点;

dp[i][j]表示给i为根的子树分配j次攻打机会可获得最大的利益,答案就为dp[root][m];

由于必须打父亲,才能打儿子,所以儿子可分配的机会为j-1,但0号点是我们假设的城堡,故他的子树有j次机会,特判一下,然后做一个分组背包就好了,因为一个子树分配的机会只能有一种方案;

抽象一下:每个子树为一组,可供选择的物品为min(siz[father], j), 容量为min(siz[son],j);

#include<bits/stdc++.h>
using namespace std;

const int maxn = 505;
int n,m;
int head[maxn],tot,dp[maxn][maxn],siz[maxn];
struct edge{
    int to,nxt,w;
}G[maxn + 100];
void add(int u,int v,int w){
    G[++tot].nxt = head[u];
    G[tot].to = v;
    G[tot].w = w;
    head[u] = tot;
}
void dfs(int u){
    for(int i = head[u]; i; i = G[i].nxt){
        int v = G[i].to;
        dfs(v); 
        int V = min(siz[u], m);
        int K = min(siz[v], m);
        for(int s = V; s >= 1; s--)
            for(int j = 1; j <= ( u == 0 ? min(s, K) : min(s-1, K) ); j++){
                dp[u][s] = max( dp[u][s], dp[u][s - j] + dp[v][j] + G[i].w);
            }
                
            
    }
}
void get_size(int u){
    siz[u] = 1;
    for(int i = head[u]; i; i = G[i].nxt){
        int v = G[i].to;
        get_size(v);
        siz[u] += siz[v];
    }
}


int main(){
    freopen("treasure.in","r",stdin);
    freopen("treasure.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++){
        int fa,w;
        scanf("%d%d",&fa,&w);
        add(fa, i, w);        
    }
    get_size(0);
    dfs(0);
    cout<<dp[0][m]<<endl;
}

考试突然不会分组背包了,╮(╯▽╰)╭, 一定要记得容量放外面,从大到小啊

以上是关于18寒假12测的主要内容,如果未能解决你的问题,请参考以下文章

寒假时间安排

寒假中期考核个人总结

寒假每一天

18寒假第六测

寒假实验2-1:计算级数

寒假日记