Processing math: 100%

Test 2018-07-19 二中集训

铁塔

(tower.pas/c/cpp)

原题:TYVJPoetize8Tower

戳我进TYVJ原题

题目描述

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

输入格式

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

输出格式

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

样例输入

 8
 1 9 9 4 1 2 2 9 

样例输出

 3 

样例解释

样例可分成 19941229 各组的和分别为 19999 ,单调不减。因 此输出 n 最大组数 =3

数据范围与约定

对于30%的数据,0<n100
对于70%的数据,0<n5000
对于100%的数据,0<n2000000<h[i]2147483647h均为随机生成。

题解

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

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

戳我进洛谷原题

题目描述

Mark 在无意中了解到了 Elf 的身世。在和 James 商量过之后,好心的他们 打算送 Elf 返回故乡。然而,去往 Gliese 的飞船票价高的惊人,他们暂时还付 不起这笔费用。经过一番考虑,Mark 打算去额外做一些工作来获得收入。 经过一番调查,Mark 发现有 N 个工作可以做。做第 i 件工作所需要的时 间为 Di ,同时也需要一个能力值 Ci 才可以去做,每件工作都可以在任意时间开 始,也可以做任意多次。所有的工作给付的报酬都是一致的。同时,有 S 个课 程可以参加,我们认为今天是第 0 天,第 i 个课程在第 Mi 天开始,持续时间 为 Li 天,课程结束之后能力值会变为 Ai 。现在 Mark 的能力值为 1Mark 只 能做工作到第 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 次。 第 10Mark 不可以继续做工作了。所以 Mark 最多做 6 次工作。

数据范围与约定

对于 20 的数据,T,S,N10 。 对于 50 的数据,T,N1000 。 对于 100 的数据,S100M,L10000A100N10000C100D10000T10000

题解

  • 动态规划,定义 f[i][j] 代表在i时间,能力值为j的最多工作次数。
  • 对应最后三种选择:
  • ①不作为 f[i][j]=f[i1][j] ,
  • ②上课 f[i][j]=f[ 上课前一个时刻 ][ 任意 ] ,
  • ③做工作 f[i][j]=f[ip[j]][j]+1p[j] 为能力值 j 的工作一次的最短用时
  • 对于②可以在预处理出k[i][j] 在i时刻结束,能力值达到j的课程的最晚开始时间。dp 过程中处理出 g[i]=maxf[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个整数 NMK ,分别表示树(兔子)的个数、路径数、计划 建造的树洞数。 接下来 M 行每行三个整数 x,y ,表示第 x 棵树和第 y 棵树之间有一条路径相 连。1x,yxy 任意两棵树之间至多只有 1 条路径。

输出格式

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

样例输入

  5 5 2 1 2 2 3 3 1 1 4 4 5 

样例输出

  1 

数据范围与约定

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

题解

  • 二分答案。

  • 枚举距离特殊点最近的建造的树洞是哪一个,记为 X

  • 在图中删除能够在二分的时间内到达该树洞 X 的所有点。

  • 此时图变为若干条独立的链,直接求最少需要的树洞数。

  • 公式为 (nX)/2X 向上取整。

  • 代表了对于一条链,每 2X+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 @   potrem  阅读(374)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示