Test 2018-07-19 二中集训

铁塔 $ (tower.pas/c/cpp) $

原题:$ TYVJ 「Poetize8」Tower $

$ \rightarrow $ 戳我看原题

  • 题目描述

$ Rainbow $ 和 $ Freda $ 要在 $ Poetic Island $ 市的一座山脚下盖房子定居了……盖房 子需要钢材,幸运的是,这里有排成一行的 $ n $ 座废弃的铁塔,从左到右编号为 $ 1 $ 到 $ n $ ,其中第 $ i $ 座的高度为 $ h[i] $ 。 $ Rainbow $ 和 $ Freda $ 想盖一座上面小下面大的城堡,并且城堡的层数尽可能多。 因此,他们要把这些铁塔分成尽量多组,每组内的铁塔编号必须是连续的,并且 从左到右各组内铁塔的高度之和单调不减。最后,他们会用每组铁塔所提供的钢 材构成一层城堡。 但是 $ Rainbow $ 和 $ Freda $ 简直弱爆了有木有,于是请你帮忙计算一下最多能分 成多少组呢?

  • 输入格式

第一行一个整数 $ n $ 。 第二行 $ n $ 个整数,第 $ i $ 个整数表示 $ h[i] $ 。

  • 输出格式

输出一个整数,表示($ n - $ 最多能分成的组数)。

  • 样例输入
 8 1 9 9 4 1 2 2 9 
  • 样例输出
 3 
  • 样例解释

样例可分成 $ 1、9、9、4 1 2 2、9, $ 各组的和分别为 $ 1 9 9 9 9 $ ,单调不减。因 此输出 $ n- $ 最大组数 $ =3 $。

  • 数据范围与约定

对于30%的数据,$ 0< n \le 100 $ 。
对于70%的数据,$ 0< n \le 5000 $ 。
对于100%的数据,$ 0< n \le 200000,0< h[i] \le 2147483647,h $均为随机生成。

  • 官方题解

这道题最先想到的就是贪心,但是纯贪心明显是不对的,
如 $ 2 2 1 3 3 $ 贪心结果为 $ 2 2 (133)$ 但实际是$ 2 (21) 3 3 $ 。
所以这样是不对的。
那要怎么做呢.....考虑用 $ dp $ .........
阶段应该是明显的就是第几个数,我们还是要用到贪心的思想,
就是保证在最后面的合起来的数尽可能的小
我们用 $ f[i] $ 表示到第i这个数的最多的组数。
$ b[i] $ 表示从1到i的所有数的和(很明显如果合并从 $ k $ 到 $ i $ 那么合并后的数就是 $ b[i]-b[k] $ );
$ s[i] $ 表示到第i这个阶段的最后一个数的大小。
所以转移方程就是:$ f[i]=max(f[k]+1); (b[i]-b[k] \ge s[k]) $
$ s[i]=b[i]-b[k]; $

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
int n,h,f[200010];
ll b[200010],s[200010];
int main(){
    scanf("%d",&n);
    for(int h,i=1;i<=n;++i){ scanf("%d",&h); b[i]=b[i-1]+h; }
    for(int i=1;i<=n;++i)
        for(int k=i-1;k>=0;--k)
            if(b[i]-b[k]>=s[k]){
                f[i]=f[k]+1;
                s[i]=b[i]-b[k];
                break;
            }
    printf("%d",n-f[n]);
    return 0;
}

工作计划 $ (work.pas/c/cpp) $

  • 题目描述

$ Mark $ 在无意中了解到了 Elf 的身世。在和 $ James $ 商量过之后,好心的他们 打算送 $ Elf $ 返回故乡。然而,去往 $ Gliese $ 的飞船票价高的惊人,他们暂时还付 不起这笔费用。经过一番考虑,$ Mark $ 打算去额外做一些工作来获得收入。 经过一番调查,$ Mark $ 发现有 $ N $ 个工作可以做。做第 $ i $ 件工作所需要的时 间为 $ Di $ ,同时也需要一个能力值 $ Ci $ 才可以去做,每件工作都可以在任意时间开 始,也可以做任意多次。所有的工作给付的报酬都是一致的。同时,有 $ S $ 个课 程可以参加,我们认为今天是第 $ 0 $ 天,第 $ i $ 个课程在第 Mi 天开始,持续时间 为 $ Li $ 天,课程结束之后能力值会变为 $ Ai $ 。现在 $ Mark $ 的能力值为 $ 1 $ 。$ Mark $ 只 能做工作到第 $ T $ 天(因为那是飞船起飞的日子)。 他想知道期限内他最多可以做多少件工作,好决定未来的打算。于是他找到 了 $ applepi $ 。でも、$ applepi $ は彼女と一緒に楽しむことが大切だ,所以这个任务 就交给你了。

  • 输入格式

第一行包含三个空格分隔的整数 $ T,S,N $ 。 之后 $ S $ 行,每行三个整数 $ M,L,A $ ,描述一个课程。 之后 $ N $ 行,每行两个整数 $ C,D $ ,描述一件工作。

  • 输出格式

一个整数,表示 $ Mark $ 最多可以做多少件工作。

  • 样例输入
  10 1 2 3 2 5 4 1 1 3
  • 样例输出
  6
  • 样例解释

第 $ 0 $ 天至第 $ 2 $ 天做第二件工作 $ 1 $ 次, 第 $ 3 $ 天至第 $ 4 $ 天参加课程,能力值变为 $ 5 $ 。然后第 $ 5 $ 天至第 $ 9 $ 天做第一件
工作 $ 5 $ 次。 第 $ 10 $ 天 $ Mark $ 不可以继续做工作了。所以 $ Mark $ 最多做 6 次工作。

  • 数据范围与约定

对于 $ 20 % $ 的数据,$ T,S,N≤10 $ 。 对于 $ 50% $ 的数据,$ T,N≤1000 $ 。 对于 $ 100% $ 的数据,$ S≤100,M,L≤10000,A≤100。N≤10000,C≤100, D≤10000,T≤10000 $ 。

  • 官方题解

动态规划,定义 $ f[i][j] $ 代表在i时间,能力值为j的最多工作次数。
对应最后三种选择:
①不作为 $ f[i][j]=f[i-1][j] $ ,
②上课 $ f[i][j]=f[ $ 上课前一个时刻 $ ][ $ 任意 $ ] $ ,
③做工作 $ f[i][j]=f[i-p[j]][j]+1 (p[j] $ 为能力值 $ \le j $ 的工作一次的最短用时 $ )$ 。
对于②可以在预处理出$ k[i][j] $ 在i时刻结束,能力值达到j的课程的最晚开始时间。$ dp $ 过程中处理出 $ g[i]=max{f[i][j]} $。
$ g[t] $ 即为答案。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int t,s,n,p[105],g[10005],f[10005][105],k[10005][105];
int main(){
    memset(p,0x3f,sizeof(p));
    memset(f,-0x3f,sizeof(f));
    scanf("%d %d %d",&t,&s,&n);
    for(int m,l,a,i=1;i<=s;++i){
        scanf("%d %d %d",&m,&l,&a);
        k[m+l-1][a]=m;
    }
    for(int c,d,i=1;i<=n;++i){
        scanf("%d %d",&c,&d);
        for(int j=c;j<=100;++j)
            p[j]=min(p[j],d);
    }
    f[0][1]=0; 
    for(int i=1;i<=t;++i)
        for(int j=1;j<=100;++j){
            f[i][j]=f[i-1][j];
            if(k[i-1][j]) f[i][j]=max(f[i][j],g[k[i-1][j]]);
            if(i-p[j]>=0) f[i][j]=max(f[i][j],f[i-p[j]][j]+1);
            g[i]=max(g[i],f[i][j]);
        }
    printf("%d",g[t]);
    return 0;
}

树洞 $ (holes.pas/c/cpp) $

原题: jzoj4896 / CH Round #72 - NOIP 夏季划水赛 (没有找到链接)

  • 题目描述

在一片栖息地上有 $ N $ 棵树,每棵树下住着一只兔子,有 $ M $ 条路径连接这些 树。更特殊地是,只有一棵树有 $ 3 $ 条或更多的路径与它相连,其它的树只有 $ 1 $ 条或 $ 2 $ 条路径与其相连。换句话讲,这些树和树之间的路径构成一张 $ N $ 个点、 $ M $ 条边的无向连通图,而度数大于 $ 2 $ 的点至多有 $ 1 $ 个。 近年以来,栖息地频繁收到人类的侵扰。兔子们联合起来召开了一场会议, 决定在其中 $ K $ 棵树上建造树洞。当危险来临时,每只兔子均会同时前往距离它 最近的树洞躲避,路程中花费的时间在数值上等于距离。为了在最短的时间内让 所有兔子脱离危险,请你安排一种建造树洞的方式,使最后一只到达树洞的兔子 所花费的时间尽量少。

  • 输入格式

第一行有 $ 3 $个整数 $ N,M,K $ ,分别表示树(兔子)的个数、路径数、计划 建造的树洞数。 接下来 $ M $ 行每行三个整数 $ x,y $ ,表示第 $ x $ 棵树和第 $ y $ 棵树之间有一条路径相 连。$ 1 \le x,y \le ,x≠y, $ 任意两棵树之间至多只有 $ 1 $ 条路径。

  • 输出格式

一个整数,表示在最优方案下,最后一只到达树洞的兔子所花费的时间。

  • 样例输入
  5 5 2 1 2 2 3 3 1 1 4 4 5 
  • 样例输出
  1 
  • 数据范围与约定

对于 $ 20 % $ 的数据,$ 1 ≤  n ≤ 10 $ 。 对于另外 $ 30 % $ 的数据,每棵树至多与 $ 2 $ 条路径相连。
对于另外 $ 30 % $ 的数据,保证存在一种最优解,使与 $ 3 $ 条或更多路径相连的树 上一定建造了树洞。
对于 $ 100 % $ 的数据,$ 1 ≤ n ≤ 2000,n-1<=m<=n*(n-1)/2 $ 。

  • 官方题解

二分答案。
枚举距离特殊点最近的建造的树洞是哪一个,记为 $ X $ 。
在图中删除能够在二分的时间内到达该树洞 $ X $ 的所有点。
此时图变为若干条独立的链,直接求最少需要的树洞数。

公式为 $ (n-X)/2*X $ 向上取整。

代表了对于一条链,每 $ 2*X+1 $ 段就会有一个树洞。

在所有枚举的情况中取最小值,与 $ K $ 比较确定二分范围变化。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define maxn 2005
#define inf 1e9+7
vector<int>E[maxn];
int n,m,k,deg[maxn],root,ans;
int dis[maxn];
inline void bfs(int st){
    memset(dis,-1,sizeof(int)*(n+1)); queue<int>q;
    q.push(st); dis[st]=0;
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=0;i<E[u].size();++i)
            if(dis[E[u][i]]==-1){
                dis[E[u][i]]=dis[u]+1;
                q.push(E[u][i]);
            }
    }
}
int len;
bool vis[maxn];
void dfs(int u){
    vis[u]=1; ++len;
    for(int i=0;i<E[u].size();++i)
        if(!vis[E[u][i]]) dfs(E[u][i]);
}
bool check(int x){
    int nowans=inf;
    for(int u=1;u<=n;++u){
        bfs(u);
        int res=0;
        if(dis[root]>x) continue;
        memset(vis,0,sizeof(bool)*(n+1));
        for(int i=1;i<=n;++i) if(dis[i]<=x) vis[i]=1;
        for(int i=1;i<=n;++i)
            if(!vis[i]){
                len=0;
                dfs(i);
                res+=(len-1)/(2*x+1)+1;
            }
        nowans=min(nowans,res+1);
    }
    return nowans<=k;
}
int main(){
    freopen("holes10.in","r",stdin);
    scanf("%d %d %d",&n,&m,&k);
    for(int u,v,i=1;i<=m;++i){
        scanf("%d %d",&u,&v);
        E[u].push_back(v);
        E[v].push_back(u);
        ++deg[u]; ++deg[v];
        if(deg[u]>3){ root=i; }
        if(deg[v]>3){ root=i; }
    }
    if(!root){
        printf("%d",(n-k-1)/(2*k)+1);
        return 0;
    }
    if(n==k){ puts("0"); return 0; }
    int l=1,r=n;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)){ ans=mid; r=mid-1; }
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PotremZ/p/9426993.html