自己社内出的一次小考。。。
感觉身体被掏空。。。知道是dp,但dp代码完全打不出来。。。。。。
深深地感到了恐惧。。。。。。
然后我就打的暴搜
2道题,暴搜出58分,竟然还能在53人中得14名。。。。。。
— — — — — — — — — — — — — — — 分割线 — — — — — — — — — — — — — —
总结一下 其实我觉得,暴搜才是王道,dp都是辣鸡
1、尽量打正解,不会才打暴搜,虽然还是可以骗到些分,而且还不错。
2、提高代码的首正率,不要花10分钟打代码,30分钟来调试
3、思维要尽量拓宽,不要局限于想到的第一种方法,多想几种,也许第一种是错的
— — — — — — — — — — — — — — — — 分割线 — — — — — — — — — — — — — — — —
下面是两道题 其实都是改编的
题目大意:
1、monkey
有n棵树,从左到右依次有它们的高度,现在有一群猴子从第一棵树开始,都要前往最后一棵树
已知第i只猴子的最长跳跃距离是k[i],也就是说,这只猴子最多能从s跳到s+k[i]
如果这只猴子落点的树的高度大于等于TA原来的那棵树的高度,TA的疲劳值就会加1
球每一只猴子跳到最后一棵树至少要累积多少疲劳值
2、telephonewire
有一排电线杆,各自有自己的高度height[i],现在我们需要把这一排电线杆顺次连接起来
每连接一条电线所花的费用是TA与旁边的电线杆的高度差*c
当然,我们也可以花x*x的代价时某根电线杆升高x米
求所需的最小费用
其实这里还有更完整的::
第一题:monkey
时限:1s 内存256MB
【题目描述】有Q只猴子要从第一棵树到第n棵树去,第i只猴子一次跳跃的最远距离为Ki。如果它在第x棵树,那它最远可以跳到第x+Ki棵树。如果第j棵树的高度比第i棵树高或相等,那么它从第i棵树直接跳到第j棵树,它的劳累值会增加1。所有猴子一开始在第一棵树,请问每只猴子要跳到第n棵树花费的劳累值最小。
输入格式:
第一行一个整数n,表示有n棵树。(2<=n<=1000000)
接下来第二行给出n个正整数D1,D2,……,Dn(1<=Di<=10^9),其中Di表示第i棵树的高度。
第三行给出了一个整数Q(1<=Q<=25),接下来Q行,给出了每只猴子一次跳跃的最远距离Ki(1<=Ki<=N-1)。
输出格式:
输出Q行,每行一个整数,表示一只猴子的最小的劳累值。
输入样例:
9
4 6 3 6 3 7 2 6 5
2
2
5
输出样例:
2
1
第二题:架设电话线(telephonewire)
时限1s,内存128M
【问题描述】Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线。 新的电话线架设在已有的N(2 <= N <= 100,000)根电话线杆上, 第i根电话线杆的高度为heighti米(1 <= heighti <= 100)。 电话线总是从一根电话线杆的顶端被引到相邻的那根的顶端 如果这两根电话线杆的高度不同,那么FJ就必须为此支付 C*电话线杆高度差(1 <= C <= 100)的费用。当然,你不能移动电话线杆, 只能按原有的顺序在相邻杆间架设电话线。Farmer John认为 加高某些电话线杆能减少架设电话线的总花费,尽管这项工作也需要支出一定的费用。 更准确地,如果他把一根电话线杆加高X米的话,他得为此付出X^2的费用。 请你帮Farmer John计算一下,如果合理地进行这两种工作,他最少要在这个电话线改造工程上花多少钱。
【输入格式】
* 第1行: 2个用空格隔开的整数:N和C
* 第2..N+1行: 第i+1行仅有一个整数:height_i
【输出格式】
* 第1行: 输出Farmer John完成电话线改造工程所需要的最小花费
【输入样例】
5 2
2
3
5
1
4
输出样例:
15
样例说明:
一共有5根电话线杆,在杆间拉电话线的费用是每米高度差$2。
在改造之前,电话线杆的高度依次为2,3,5,1,4米。
最好的改造方法是:Farmer John把第一根电话线杆加高1米,把第四根加高2米,
使得它们的高度依次为3,3,5,3,4米。这样花在加高电线杆上的钱是$5。
此时,拉电话线的费用为$2*(0+2+2+1) = $10,总花费为$15。
— — — — — — — — — — — — — — — — 分割线 — — — — — — — — — — — — — — — —
以下是题解::
第一题:
f[i]表示这只猴子跳到第i棵树所花的最小疲劳值,那么
f [ i ] = min ( f [ i ] , f [ j ] + ( h [ i ] > h [ j ] ) )
其中 i 1 ~ n,j i - m ~ i - 1
然后我们发现其实这个树的高度其实是可以用一个单调队列来记录一下的
然后就可以把时间复杂度缩为O(nq)
以下是代码:
#include<cstdio>
#include<cstring>
#include<deque>
using namespace std;
inline void read(int &x) {
x=0;
int f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(int x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>9)
pr(x/10);
putchar(x%10+48);
}//快读快输不解释
int a[1000005],dp[1000005];
int i,n,j,k,z,m;
deque<int>q;
int main() {
read(n);
for(i=1;i<=n;i++)
read(a[i]);
read(z);
for(i=1;i<=z;i++) {
read(m);
q.push_back(1);//第一棵树
for(j=2;j<=n;j++) {//从第二棵树到第n棵树
while(!q.empty()&&q.front()<j-m)//不能从这棵树跳到此树
q.pop_front();//没有用了,pop掉
dp[j]=dp[q.front()]+(a[j]>=a[q.front()]);//计算dp的值,括号里判断疲劳值加不加一
while(!q.empty())
if(dp[q.back()]>dp[j]||(dp[q.back()]==dp[j]&&a[j]>a[q.back()]))
//疲劳值更小或者疲劳值相同并且高度更高
q.pop_back();//pop出去
else//不然就可以直接停止了
break;
q.push_back(j);
}
pr(dp[n]),putchar('\n');
q.clear();
memset(dp,0,sizeof(dp));//别忘了清空
}
}
第二题:
dp[i][j]表示第i根电线杆的高度是j米时前面所有的费用的最小值
最基础的状态转移方程:
f [ i ] [ j ] = ( j - h[ i ] ) * ( j - h[ i ] ) + min ( f [ i-1 ] [ k ] + c * | j - k | )
我们把这个方程里的绝对值去掉,就可以得到
1、j>=k
f [ i ] [ j ] = ( j - h [ i ] ) * ( j - h [ i ] ) + min ( f [ i-1 ] [ k ] - c * k + c * j )
2、j<=k
f [ i ] [ j ] = ( j - h[ i ] ) * ( j - h [ i ] ) + min ( f [ i - 1 ] [ k ] + c * k - c * j )
这里的等号放在哪里都可以
在整理一下:
1、 f [ i ] [ j ] = ( j - h [ i ] ) ^ 2 + c * j + min ( f [ i - 1 ] [ k ] - c * k )
2、 f [ i ] [ j ] = ( j - h [ i ] ) ^ 2 - c * j + min ( f [ i - 1 ] [ k ] + c * k )
我们发现后面的这个min其实可以记录一下,j 循环时再去更新min
这样就可以把最里面的一层O ( n ) 的缩为O ( 1 ),所以总时间复杂度是O ( n * maxh )
再上代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
inline void read(int &x) {
x=0;
int f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(int x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>9)
pr(x/10);
putchar(x%10+48);
}//快读快输不解释
int a[100005],dp[100005][105],i,n,j,k,c,m,minn;
int main() {
read(n),read(c);
memset(dp,0x3f3f3f3f,sizeof(dp));//把所有的dp清为最大值,方便计算
for(i=1;i<=n;i++)
read(a[i]),m=max(m,a[i]);//这里m取所有高度的最大值是为了节省时间
//因为如果我们把某一个电线杆升高到m以上,那就是白费力气
for(i=a[1];i<=m;i++)
dp[1][i]=(i-a[1])*(i-a[1]);//初始化,更新第一根电线杆高度为i时的费用
for(i=2;i<=n;i++) {
minn=2147483647;//每次都要更新
for(j=0;j<a[i];j++)
minn=min(minn,dp[i-1][j]-c*j);//因为只能升高,这里相当于预处理一次
for(j=a[i];j<=m;j++) {//顺着更新一次
dp[i][j]=minn+c*j+(j-a[i])*(j-a[i]);
minn=min(minn,dp[i-1][j]-c*j);
}
minn=2147483647;
for(j=m;j>=a[i];j--) {//倒着更新一次
minn=min(minn,dp[i-1][j]+c*j);
dp[i][j]=min(dp[i][j],minn-c*j+(j-a[i])*(j-a[i]));//状态转移方程
}
}
minn=2147483647;
for(i=a[n];i<=m;i++)
minn=min(dp[n][i],minn);//根据dp的意思,在最后一根电线杆的高度上找min值
pr(minn);
}