Test 2018-07-19 二中集训

铁塔

$ (tower.pas/c/cpp) $

原题:$ TYVJ 「Poetize8」Tower $

$ \rightarrow $ 戳我进TYVJ原题

题目描述

$ 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);\quad (b[i]-b[k] \ge s[k]) \quad 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) $

原题:luogu P2948 [USACO09OPEN]滑雪课Ski Lessons

$ \rightarrow $ 戳我进洛谷原题

题目描述

$ 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]=max(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;
}
posted @ 2018-08-05 21:53  potrem  阅读(372)  评论(0编辑  收藏  举报