• AtCoder Beginner Contest 145


    传送门

    A - Circle

    签到。

    B - Echo

    签到到。

    C - Average Length

    要卡下精度,可用二分或者long double来搞。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/16 20:04:44
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 10;
    const double eps = 1e-6;
    int n;
    struct Point{
        int x, y;   
    }p[N];
    int a[N];
    double dis(Point A, Point B) {
        double tmp = 1.0 * (A.x - B.x) * (A.x - B.x) + 1.0 * (A.y - B.y) * (A.y - B.y);   
        double l = 0, r = tmp, mid;
        for(int i = 1; i <= 500; i++) {
            mid = (l + r) / 2;
            if(mid * mid < tmp) l = mid;
            else r = mid;   
        }
        return r;
    }
    void run(){
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y;
        for(int i = 1; i <= n; i++) a[i] = i;
        double ans = 0;
        int tot = 0;
        do {
            ++tot;
            for(int i = 2; i <= n; i++) {
                ans += dis(p[a[i]], p[a[i - 1]]);
            }
        } while(next_permutation(a + 1, a + n + 1));
        ans = ans / tot;
        cout << ans;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
    	return 0;
    }
    
    

    D - Knight

    题意:
    现在有个棋子位于((0,0))点,当棋子位于((i,j))时,可以跳向((i+1,j+2),(i+2,j+1))这两个格子。
    问有多少种方式可以到点((x,y))

    思路:
    由于(x,yleq 10^6),显然直接(dp)不行。
    然后找规律,从终点往回跳来模拟一下,发现最终会形成多条斜率为(1)的直线,每跳直线上面相关点的答案为一个组合数。
    然后发现最终((0,0))点所在的层数为(frac{x+y}{3}),最终答案就是(frac{x+y}{3}choose t)
    细节在纸上画一下就出来了。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/16 20:26:13
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e6 + 15, MOD = 1e9 + 7;
    
    int fac[N];
    
    ll qpow(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;   
        }
        return ans;   
    }
    
    int C(int n, int m) {
        return 1ll * fac[n] * qpow(fac[n - m], MOD - 2) % MOD * qpow(fac[m], MOD - 2) % MOD;
    }
    
    void run(){
        fac[0] = 1;
        for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
        int x, y;
        cin >> x >> y;
        if((x + y) % 3 != 0) {
            cout << 0;
            return;
        }
        int t = (x + y) / 3;
        if(y - t >= 0 && y - t <= t) {
            cout << C(t, y - t);
        } else cout << 0;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
    	return 0;
    }
    
    

    E - All-you-can-eat

    题意:
    现在有(n)盘菜,每盘菜需要(a_i)的时间去吃,有(b_i)的美味度。
    现在有如下规则:

    • 点了一盘菜之后,只能将其吃完后才能点下一盘菜。
    • (T)时刻过后就不能再点菜了,但是依旧可以吃菜。
    • 每种菜只能点一次。

    最后问最后能够得到的最大美味度是多少。

    思路:

    • 注意比较重要的一点,无论最终点菜顺序是什么,我们都可以将某一份菜安排在(T)时刻来点。
    • 那么之后问题就变得很简单了:总共有(T-1)时刻,然后相当于一个背包问题,直接(dp)需要(O(n^2))
    • 但是我们需要枚举哪个菜在最后点,所以总复杂度为(O(n^3))的,显然时间复杂度不能承受。
    • 由于我们只关注的是某一个菜不选,那么我们可以预处理一个前后缀的(dp)(dp1[i][j])表示(1)~(i)的物品中,总时间不超过(j)的最大美味度,(dp2[i][j])同理。
    • 最终时间复杂度为(O(n^2))

    直接来做这个题因为可以超出时间限制,所以不好定义状态,但是钦定最后一个位置后问题就得到转换。
    前后缀(dp)预处理还是比较巧妙QAQ。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/18 15:30:25
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 3005;
    
    int n, t;
    int a[N], b[N];
    int dp1[N][N], dp2[N][N];
    
    void run(){
        for(int i = 1; i <= n; i++) cin >> a[i] >> b[i];
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= t - 1; j++) {
                dp1[i][j] = dp1[i - 1][j];
                if(j >= a[i]) 
                    dp1[i][j] = max(dp1[i][j], dp1[i - 1][j - a[i]] + b[i]);
            }   
        }
        for(int i = n; i >= 1; i--) {
            for(int j = 1; j <= t - 1; j++) {
                dp2[i][j] = dp2[i + 1][j];
                if(j >= a[i])
                    dp2[i][j] = max(dp2[i][j], dp2[i + 1][j - a[i]] + b[i]);   
            }
        }
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j <= t - 1; j++) {
                ans = max(ans, dp1[i - 1][j] + dp2[i + 1][t - 1 - j] + b[i]);
            }   
        }
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n >> t) run();
    	return 0;
    }
    
    

     
    这个题还有更巧妙的解法:假设我们知道了最后点的哪些菜,那么显然我们总能把耗时最多的安排在(T)时刻来点。
    这时问题转换地更进一步:假设钦定了最后一位,那么前面只能选择耗时不超过它的。
    所以直接处理出上面的(dp1[i][j]),同时维护一个后缀最大值即可。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/18 15:30:25
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 3005;
    
    int n, t;
    struct node {
        int a, b;   
        bool operator < (const node &A) const {
            return a < A.a;   
        }
    }p[N];
    int maxv[N];
    int dp[N][N];
    
    void run(){
        for(int i = 1; i <= n; i++) cin >> p[i].a >> p[i].b;
        sort(p + 1, p + n + 1);
        for(int i = n; i >= 1; i--) maxv[i] = max(maxv[i + 1], p[i].b);
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= t - 1; j++) {
                dp[i][j] = dp[i - 1][j];
                if(j >= p[i].a) dp[i][j] = max(dp[i][j], dp[i - 1][j - p[i].a] + p[i].b);
            }
            ans = max(ans, dp[i][t - 1] + maxv[i + 1]);
        }
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n >> t) run();
    	return 0;
    }
    
    

    F - Laminate

    题意:
    现在有(n)个柱子排在一起,每个柱子有个高度(h_i,0leq h_ileq 10^9)
    现在有至多(k)次机会任意修改某些柱子的高度。
    之后会执行操作:每次可以横向消去一段连续的柱子。问最终最少的操作次数是多少。

    思路:
    这个题感觉直接做也没什么思路...太菜了555。
    就感觉问题很抽象,考虑很多情况,一般这种感觉将问题形象化、具体化是解题的关键,比如这个题,我们考虑(k=0)的情况,那么最终的答案为:

    [sum_{i=1}^{n}max(0,h_i-h_{i-1}) ]

    然后有个结论:如果修改一根柱子,假设其位置为(i),那么最终其高度在([h_{i-1},h_{i+1}])之间是最优的。

    这个结论较为显然,那么我们可以直接钦定:修改操作即令(h_i=h_{i-1})
    进一步观察可以发现,操作等价于删除一根柱子。

    这个时候解法就呼之欲出了,可令(dp[i][j])表示前(i)根柱子,保留(j)根的最小操作次数。
    那么有转移:(dp[i][j] = min{dp[k][j-1]+max(0,h_i-h_k),j<i})
    直接(O(n^3))来搞就没了。

    P.S:这个可以将后面(max)操作打开,然后用树状数组维护两个值来优化,复杂度可以达到(O(n^2logn))

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/18 18:16:10
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 300 + 5;
    
    int n, K;
    
    int h[N];
    ll dp[N][N];
    
    void run(){
        for(int i = 1; i <= n; i++) cin >> h[i];
        memset(dp, INF, sizeof(dp));
        dp[0][0] = 0;
        for(int i = 1; i <= n - K; i++) {
            for(int j = i; j <= n; j++) {
                for(int k = 0; k < j; k++) {
                    dp[j][i] = min(dp[j][i], dp[k][i - 1] + max(0, h[j] - h[k]));   
                }
            }   
        }
        ll ans = INF;
        for(int i = 1; i <= n; i++) ans = min(ans, dp[i][n - K]);
        if(ans == INF) ans = 0;
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n >> K) run();
    	return 0;
    }
    
    
  • 相关阅读:
    DP--HDU 1003求数字串中的最大连续序列(含有DP过程详细分析)
    递归+DFS--简单迷宫问题--百练2802
    枚举--百练2812--恼人的青蛙(内含枚举基本思想总结)
    计蒜客:最大子阵
    hdu 4515 小Q系列故事——世界上最遥远的距离
    日期计算
    最大最小公倍数
    hdu 1568 Fibonacci
    矩阵快速幂
    矩阵乘法
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11887825.html
Copyright © 2020-2023  润新知