P14460 【MX-S10-T1】『FeOI-4』寻雾启示 做题笔记

思路 90pts

注意到 $O(n^2)$ 的暴力的能得到 90pts,不妨先考虑一下这个该怎么写。

我们可以考虑 dp,令 dp[i] 为到 i 的时候的最少的答案。

我们可考虑要从 dp[j] 扩展到 dp[i] 只有两种方法,第一种方法也就是在最后一次买东西的时候把羊毛都没买好,后面的直接走到目的地就行了。

第二种方法,就是在走到 j 后走回到买东西的地方,买好东西,然后再走到要搭路的地方搭路。

这样,我们就可以写出一个平方级别的暴力了。

思路 100pts

由于之前是对于所有小于 i 的 j 的两种方法取最小,那么我们可以通过打表发现假设 g(j) 表示第一种方法,f(j) 表示第二种方法,那么 g(j) 是单调递减的,f(j) 是先减后增的,那么我们可以通过二分得到 f 函数的最小值,然后就可以 log 级别得出单个点的答案了。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
/*
*/
int m, k, t1, t2;
int a[1000005];
int f(int j, int i) {
    if (j < 0) return -1;
    if (j < 0 || j > i + 1) return 1e18;
    i ++;
    // cout << i << " " << j << " ";
    return a[j] + j * t2 * 2 + max(0ll, k * (i - j) - a[j] + k * j - j * t2) + t1 * (i - j);
}
int findMinByBinarySearch(int low, int high) {
    int th = high - 1;
    while (low <= high) {
        // int mid = low + (high - low) / 2;
        int mid = (low + high + 1) / 2;
        int f_mid = f(mid, th);
        int f_left = f(mid - 1, th);
        int f_right = f(mid + 1, th);

        // std::cout << "检查点: x=" << mid << ", f(x)=" << f_mid 
        // << ", f(x-1)=" << f_left << ", f(x+1)=" << f_right << ", th=" << th << "\n";

        // 如果当前点是谷底(比两边都小)
        if (f_mid <= f_left && f_mid <= f_right) {
            // cout << f_left << " " << f_mid << " " << f_right << "\n";
            // if (f_left == f_mid) return mid - 1;
            // else if (f_right == f_mid) return mid + 1;
            // else return mid;
            return mid;
        }
        // if (f_left == -1) low = mid + 1;
        else if (f_left >= f_mid) {
            low = mid + 1;
        }
        else {
            high = mid - 1;
        }
    }
    return low;
}
void displayAllPoints(int aa) {
    std::cout << "所有数据点:" << std::endl;
    for (int i = 0; i < aa; ++i) {
        std::cout << "f(" << i << ") = " << f(i, aa - 1) << " , 其中 i = " << aa << ", j = " << i << ", f2(x)=" << a[i] + k * (aa - i) + t1 * (aa - i) << "\n";
    }
    cout << "代码算出来的最小值是:" << f(findMinByBinarySearch(0, aa), aa - 1) << "\n";
    int i = aa;
    int j = i - 1;
    // cout << a[j] + k * (i - j) + t1 * (i - j) << "\n";
    // std::cout << std::endl;
}
const int dev = 0;
void solve() {
    for (int i = 0; i <= m; i ++) a[i] = 0;
    cin >> m >> k >> t1 >> t2;
    for (int i = 1; i <= m; i ++) {
        a[i] = 1e18;
        // a[i] = (i - 1) * t2 * 2 + max(0ll, k - a[i - 1] + k * (i - 1) - (i - 1) * t2) + t1 + a[i - 1];
        int j = i - 1;
        a[i] = min(a[i], a[j] + k * (i - j) + t1 * (i - j));
        a[i] = min(a[i], f(findMinByBinarySearch(0, i), i - 1));
        // for (int j = 0; j < i; j ++) {
        //     a[i] = min(a[i], a[j] + k * (i - j) + t1 * (i - j));
        //     a[i] = min(a[i], a[j] + j * t2 * 2 + max(0ll, k * (i - j) - a[j] + k * j - j * t2) + t1 * (i - j));
        // }
        if (dev == 1) displayAllPoints(i), cout << a[i] << "\n\n";
        if (!dev) cout << a[i] << " ";
        // cout << "\n";
    }
    int i = m;
    cout << "\n";
    if (dev == 2) {
    for (int j = 0; j < i; j ++) {
        cout << a[j] + k * (i - j) + t1 * (i - j) << " " << a[j] + j * t2 * 2 + max(0ll, k * (i - j) - a[j] + k * j - j * t2) + t1 * (i - j) << "\n";
    }
    }
}
signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T; cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}
本文链接:https://ztrztr.top/archives/1059
版权声明:本文 《P14460 【MX-S10-T1】『FeOI-4』寻雾启示 做题笔记》 为 ztrztr 原创。著作权归作者所有。
转载说明:联系作者或者评论区留言获得转载授权,并注明转载地址。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇