大家来共同交流一下第一题思路。。【百度之星吧】

发布时间:2024-12-10 13:27

感觉这次比上次难,复赛A只会两道(只不过第二道没敢用网络流,用裸贪心骗分的。。)
先说一下复赛B中自己A题的算法吧:
看到题目第一反映是想写个递推,用dp解。。。但是数据量太大了。。。。。O(100000^2)显然超时。。。寻找更优化算法。。。。
于是乎,过了一会儿,发现一个性质:如果在区间(l,r)内有一个比l和r都高的点,那么这个点是必定会经过的,所以可以将原问题分解成两部分(l, pos), (pos,r)分别解这两个问题然后合并就可以了。其他情况也类似,都有这个特点:如果l可以直接到r,那么直接计算就可以了,肯定是最小值;否则得话选(l+1,r-1)中最高的一个点,然后分别求解子问题,然后合并(就是相加)就可以了。
不知道这个思路是否正确,但稍微证明了一下感觉它起码不会比最优情况差 = =!也没有举出什么反例,代码如下,如有不同解法,欢迎交流:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100010;
typedef long long llg;
llg n, ans, h[N];
int tree[N<<2];
void build_tree(int u, int l, int r)
{
    int mid = (l+r)>>1, tmp = u<<1;
    if(l == r)
    {
        tree[u] = l;
        return;
    }
    else
    {
        build_tree(tmp, l, mid);
        build_tree(tmp+1, mid+1, r);
        if(h[tree[tmp]] > h[tree[tmp+1]]) tree[u] = tree[tmp];
        else tree[u] = tree[tmp+1];
    }
}
int Getmax(int u, int l, int r, int a, int b)
{
    int tmp = u<<1, mid = (l+r)>>1;
    if(a==l && b==r) return tree[u];
    else
    {
        if(b <= mid) return Getmax(tmp, l, mid, a, b);
        else if(a > mid) return Getmax(tmp+1, mid+1, r, a, b);
        else
        {
            int tmp1, tmp2;
            tmp1 = Getmax(tmp, l, mid, a, mid);
            tmp2 = Getmax(tmp+1, mid+1, r, mid+1, b);
            if(h[tmp1] >= h[tmp2]) return tmp1;
            else return tmp2;
        }
    }
}
llg merge(int l, int r)
{
    llg tmp1, tmp2;
    if(l == r) return 0;
    if(l+1 == r)
    {
        if(h[l] >= h[r]) return 0;
        else return h[r]-h[l];
    }
    int pos = Getmax(1, 0, n, l+1, r-1);
    if(h<h[l] || h<h[r])
    {
        if(h[l]>=r-l && h[l]>=h[r]) return 0;
        if(h[l]>=r-l && h[l]<h[r]) return h[r]-h[l];
    }
    tmp1 = merge(l, pos);
    tmp2 = merge(pos, r);
    return tmp1+tmp2;
}
int main()
{
    int i;
    scanf("%lld", &n);
    for(i = 0; i <= n; i++) scanf("%lld", &h[i]);
    build_tree(1, 0, n);
    ans = merge(0, n);
    printf("%lld\n", ans);
    return 0;
}




czz19891012

直接用堆,找出之前区间里面能跳到这个点的高度最高的那个.
我瞎写的.



Moon_1st

补充一下,查找最大值时用到了线段树,所以最坏时间复杂度是O(n*logn)



无解的名字

不是堆 单调队列 单调递减的单调队列
除了入栈出栈 每次还要从队首元素开始 把不能跳到下一个点的点删除 这样每次新来的点默认取队首即可
然后倒着扫一遍。。。
我是这么想的。。。
话说就想出来一道 回家种地抱孩子去了。。。



coolypf

我用的贪心,和DP的程序对拍了半天,发现没错就叫上去了



贴吧用户_0J6N8ay

这个有反例吧。
8 3 1 1 1 1 1 1 2 7
如果按照你说的拆解方式,则是8=>3, 3=>7
这样得到的解是6
如果选择8=>2, 2=>7
那么解是5,更优。



KenKwok0

大概同六楼的算法,O(n)。
试过 Random Test Case,答案跟 LZ 的一样。
#include <cstdio>
int n, h[1 << 17], q[1 << 17], qa, qb, st;long long ans;
int main() { scanf("%d", &n) == 1; for (int i = 0; i <= n; ++i) scanf("%d", h + i); qa = qb = -1; ans = 0; st = 0; while (st != n) { for (int i = (qa == qb ? st + 1 : q[qb] + 1); i <= n && i <= st + h[st]; ++i) { if (qa == qb) q[++qb] = i; else { while (qa != qb && h[q[qb]] < h[i]) --qb; if (qa == qb || q[qb] + h[q[qb]] < i + h[i]) q[++qb] = i; } } ans += (h[st] < h[q[qa + 1]] ? h[q[qa + 1]] - h[st] : 0); st = q[++qa]; } printf("%lld\n", ans); return 0;}




tecmoo

怎么说呢,贪心和DP很难有不同点,LS给你个我生成数据的程序,要是能和DP一样就差不多OK了。
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
int main()
{
    freopen("in.txt","w",stdout);
    int i,j,k,n;
    srand(time(0));
    k=300;
    while(k--)
    {
        n=rand()%500+1;
        printf("%d\n",n);
        for(i=1;i<=n+1;i++)
            printf("%d ",(rand()%15+1));
        printf("\n");
    }
    return 0;
}




KenKwok0

十楼的 test data 厉害!我试了十几个 n=10000 的 test data 都找不到反例……



zlq_for_astar

同单调队列
我来证明下吧
可以发现 这个游戏取决于要请可能少的从上到下
不妨假定这个算法不是最优的
那么也就是说 对于某个点i 存在点j 比能到达i 的最远点k更优
首先可以发现 能到达点i的点的高度必然从头到尾单调递减
所以有H[k] > H[j]
那么不妨假设点j之前转移中第一个在k点前面的p点转移过来
很显然 有H[P] >= H[k]
那么显然有 p点到j点的代价至少是 H[p] - H[j]
然而 p点可到k之后意味着p点必然可以到k 那么p点到k的代价至多是H[p] - H[k]
由此可知k点一定比j点优
故证毕



网址:大家来共同交流一下第一题思路。。【百度之星吧】 https://mxgxt.com/news/view/125293

相关内容

百度百家号打造「明星创作者计划」,聚集百亿流量实现生态共创
百位明星连夜交付援汉第三批负压救护车 思源工程共捐赠80辆
流量的背后,社交营销思路变了
百度娱乐
古丝路交流互鉴的文明回响
丝路通世界 光影耀长安 第十一届丝绸之路国际电影节盛大开幕
发扬丝路精神 专家学者共议新时代“伊儒会通”
深度解析《明星找不同游戏大全》:追星新潮流
泛娱乐化表达法+年轻视角+社会性题材=《约吧大明星》
百度百家号「明星创作者计划」官宣启动,精品内容实现创作升维

随便看看