zoukankan      html  css  js  c++  java
  • 【8.20校内测试】【DP】【二分+贪心】

    一开始想的贪心,可是发现贪心的问题太多了啊!只能保证当前最优,全局完全无法考虑。

    所以正解是dp。预处理出前缀和,枚举每个区间,在每个点记录$now[i]$表示以$i$这个塔结尾的塔组目前的高度。$dp[i]$表示以$i$这个塔结尾最多能分成多少组。如果$dp[i]$可以更新成更优值,则直接更新$dp$和$now$值,否则如果$dp$值相同,则尽量使$now$值最小。

    #include<iostream>
    #include<cstdio>
    #define ll long long
    using namespace std;
    
    ll n, h[5005], pre[5005], now[5005], dp[5005];
    
    int main ( ) {
        freopen ( "tower.in", "r", stdin );
        freopen ( "tower.out", "w", stdout );
        scanf ( "%I64d", &n );
        for ( int i = 1; i <= n; i ++ )
            scanf ( "%I64d", &h[i] ), pre[i] = pre[i-1] + h[i];
        for ( int i = 1; i <= n; i ++ )
            for ( int j = 0; j < i; j ++ ) {
                if ( pre[i] - pre[j] >= now[j] ) {
                    if ( dp[i] < dp[j] + 1 ) dp[i] = dp[j] + 1, now[i] = pre[i] - pre[j];
                    else if ( dp[i] == dp[j] + 1 ) {
                        now[i] = min ( now[i], pre[i] - pre[j] );
                    }
                }
            }
        printf ( "%I64d", n - dp[n] );
        return 0;
    }

    显然(?)也是dp,$dp[i][j]$表示完成了$i$天,此时能力值为$j$时能做的最多工作量。$fine[i]$是预处理出的能力值为$i$时的效率最高的工作需要的时间。$qwq[i][j]$表示每个上完课后的第1天$i$,能力值为$j$,上课最短的时间。$pre[i]$表示过了$i$天可以得到的最大$dp$值。

    枚举天数,能力值。

    每次更新时可以选择不作为,即$dp[i][j]$直接从$dp[i-1][j]$转移过来。

    如果第$i$天是某次刚上完课的第二天,则可以选择上课,从上课开始之前的最优$dp$值转移过来,即上课开始那一天的$pre$值。

    如果当前天数和$fine[j]$满足可以工作,则从不工作那天转移过来,可以选择工作一次。

    【注意】$dp$值初始化是负无穷,保证如果第$i$天不能满足$j$的能力值,则一定不会被更新,或去更新$pre$值。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define RG register
    using namespace std;
    
    int T, S, N, fine[10005], qwq[10005][105], pre[10005], dp[10005][105];
    
    struct LRN {
        int m, l, a;
    } stdy[105];
    
    int main ( ) {
        freopen ( "wrk.in", "r", stdin );
        freopen ( "wrk.out", "w", stdout );
        scanf ( "%d%d%d", &T, &S, &N );
        memset ( qwq, 0x3f3f3f3f, sizeof ( qwq ) );
        for ( RG int i = 1; i <= S; i ++ ) {
            int m, l, a;
            scanf ( "%d%d%d", &m, &l, &a );
            qwq[l+m][a] = min ( qwq[l+m][a], l );
        }
        memset ( fine, 0x3f3f3f3f, sizeof ( fine ) );
        for ( RG int i = 1; i <= N; i ++ ) {
            int c, d;
            scanf ( "%d%d", &c, &d );
            fine[c] = min ( fine[c], d );
        }
        for ( int i = 1; i <= 100; i ++ )
            fine[i] = min ( fine[i], fine[i-1] );
        memset ( dp, -0x3f3f3f3f, sizeof ( dp ) );
        dp[0][1] = 0;
        for ( int i = 1; i <= T; i ++ ) {    
            for ( int j = 1; j <= 100; j ++ ) {
                dp[i][j] = dp[i-1][j];
                if ( qwq[i][j] != 0x3f3f3f3f ) dp[i][j] = max ( dp[i][j], pre[i-qwq[i][j]] );
                if ( i - fine[j] >= 0 ) dp[i][j] = max ( dp[i][j], dp[i-fine[j]][j] + 1 );
                pre[i] = max ( dp[i][j], pre[i] );
            }
        }
        printf ( "%d", pre[T] );
        return 0;
    }

    很有趣的一道题。题目给出的性质是只有一个点的度数大于等于3,先找出这个点作为root。

    bfs预处理出点距离的邻接矩阵,二分答案,每次去check的时候是选择root周围可以覆盖它的点,割掉这个点去更新答案。设当前枚举的点是$x$,先标记$x$可以覆盖的所有点,再dfs去计算没有被标记的点的数量,就是被拆分开的一条条链上的点的数量,每条链上需要的树洞是$ceil ( frac{1.0 * now}{2*len+1} )$,$now$是这条链上的点数,注意因为自己也会算进去,所以一个点能覆盖的点数是$2*len+1$。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    int n, m, k, root;
    int dis[2005][2005], d[2005];
    
    int stot, tov[4000004], nex[4000005], h[2005];
    void add ( int u, int v ) {
        tov[++stot] = v;
        nex[stot] = h[u];
        h[u] = stot;
    }
    
    queue < int > q;
    void bfs ( int s ) {
        queue < int > q; q.push ( s );
        while ( !q.empty ( ) ) {
            int x = q.front ( ); q.pop ( );
            for ( int i = h[x]; i; i = nex[i] ) {
                int v = tov[i]; d[x] ++; d[v] ++;
                if ( !dis[s][v] && v != s )
                    dis[s][v] = dis[s][x] + 1, q.push ( v );
            }
        }
    }
    
    int now, v[2005];
    
    void dfs ( int u ) {
        now += ( v[u] = 1 );
        for ( int i = h[u]; i; i = nex[i] )
            if ( !v[tov[i]] )
                dfs ( tov[i] );
    }
    
    int work ( int x, int len ) {
        int s = 0;
        memset ( v, 0, sizeof ( v ) );
        for ( int i = 1; i <= n; i ++ )
            v[i] = ( dis[i][x] <= len );
        for ( int i = 1; i <= n; i ++ )
            if ( !v[i] ) now = 0, dfs ( i ), s += ceil ( 1.0 * now / ( 2 * len + 1 ) );
        return s;
    }
    
    bool check ( int x ) {
        int ans = 0x3f3f3f3f;
        for ( int i = 1; i <= n; i ++ )
            if ( dis[root][i] <= x ) {
                ans = min ( ans, work ( i, x ) );
            }
        return ans < k;
    }
    
    int main ( ) {
        freopen ( "holes.in", "r", stdin );
        freopen ( "holes.out", "w", stdout );
        scanf ( "%d%d%d", &n, &m, &k );
        for ( int i = 1; i <= m; i ++ ) {
            int x, y;
            scanf ( "%d%d", &x, &y );
            add ( x, y ); add ( y, x );
        }
        for ( int i = 1; i <= n; i ++ ) {
            bfs ( i );
            if ( d[i] > 2 ) root = i;
        }
        if ( !root ) {
            cout << ceil ( 1.0 * ( n - k ) / ( k << 1 ) );
            return 0;
        }
        int l = 0, r = n, ans;
        while ( l <= r ) {
            int mid = ( l + r ) >> 1;
            if ( check ( mid ) ) r = mid - 1, ans = mid;
            else l = mid + 1;
        }
        cout << ans;
        return 0;
    }
  • 相关阅读:
    十、 Spring Boot Shiro 权限管理
    十六、Spring Boot 部署与服务配置
    十五、Spring Boot 环境变量读取 和 属性对象的绑定
    三、spring cloud 服务提供与调用
    CSS 表格实例
    CSS 列表实例
    CSS 内边距 (padding) 实例
    CSS 外边距
    CSS 边框(border)实例
    CSS 字体(font)实例
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9506827.html
Copyright © 2011-2022 走看看