铁塔
$ (tower.pas/c/cpp) $
原题:$ TYVJ 「Poetize8」Tower $
题目描述
$ 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
题目描述
$ 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;
}