首页 > 其他分享 >「UOJ811」璀璨宝石

「UOJ811」璀璨宝石

时间:2023-07-17 19:34:50浏览次数:51  
标签:tmp 宝石 rep int ChkMin UOJ811 inline 璀璨

题目

点这里看题目。


题面太长,我懒得抄了。

分析

假设五种宝石最终需要的数量为 \(A,B,C,D,E\),则取宝石需要的操作轮数为 \(\max\{A,B,C,D,E,\lceil\frac{A+B+C+D+E}{2}\rceil\}\)。

钦定 \(A,B,C,D,E\) 成为上式的最大值是容易的,但要控制 \(\lceil\frac{A+B+C+D+E}{2}\rceil\) 成为最大值却不容易。注意到,后一种情况计算时只需要知道 \(A+B+C+D+E\),而这个值小到可以直接枚举(或者说塞进状态里),所以考虑求出 \(A,B,C,D,E\) 分别为上式最大值时的操作方案数量,一减就可以得到后一种情况的数量了。复杂度为 \(O(n^2M^2)\)。

虽然复杂度不是很好,但是确实很有意思。


另一种想法就是在 DP 过程中算出来最小的取宝石次数。

很简单的错误的思路:每次任意地取出来两个不同的宝石,最后可能剩下一种宝石,那就单独取它。之所以它是错的,是因为这个取法就等价于每次任意取......

但是这个操作起来真的很简单啊!怎么修一下这个做法呢?

如果任意取的过程可以时刻保证 \(2\max<\sigma\),那么这个任意取的次数就是最小次数。反过来,如果任意取的过程中的某一时刻 \(2\max\ge \sigma\),那么我们就应该开始将最多的一种和别的宝石一起取了。可操作的地方就来了:我们可以任意地钦定什么时候开始做 \(2\max\ge \sigma\) 的操作,显然在不合适的时候进行操作方式的切换的话操作数量不会变小。更进一步地,我们也可以必要的时候,选择从 \(2\max\ge \sigma\) 的操作切换回任意选的操作。当然,为了保证取得到最小值,我们需要包含每一种必要的切换策略。

那么,考虑 \(f_{i,j,t,k}\) 表示任意选过程中,当前桌子上的两张牌为 \(i,j\),且目前剩下了 \(k\) 个第 \(t\) 种宝石没有取得时最小取宝石轮数;\(g_{i,j,t,k}\) 表示认为第 \(t\) 种宝石数量会不少于一半时,当前桌子上两张牌为 \(i,j\),且其它类型的宝石总共剩 \(k\) 个没有取得时的最小取宝石轮数。

我们的策略同加入宝石的顺序无关,因此我们只需要考虑一次性加入多个同种宝石的转移。也就是:

  • 考虑在 \(f\) 的 \((t,k)\) 状态时,加入 \(b\) 个 \(a\) 种宝石。

    • 如果 \(t=b\),那么宝石数量加起来,转移到 \(f\) 的 \((t,k+b)\) 状态。

    • 如果 \(t\neq b\),还有选择:

      • 一种选择是任意取。那么当 \(k\le b\) 的时候,可以转移到 \(f\) 的 \((a,b-k)\) 状态;当 \(k>b\) 的时候,则可以转移到 \(f\) 的 \((t,k-b)\) 状态。

      • 另一种选择则是,先任意取一些宝石,然后切换到 \(2\max\ge\sigma\) 的操作。也即,先取 \(x\) 对宝石(应有 \(0\le x\le \min\{k,b\}\)),然后钦定 \(t'\)(应有 \(t'\neq t,t'\neq a\))为最多的宝石,最终转移到 \(g\) 的 \((t',b+k-2x)\)。

  • 考虑在 \(g\) 的 \((t,k)\) 状态时,加入 \(b\) 个 \(a\) 种宝石。

    • 如果 \(a\neq t\),那么宝石数量加起来,转移到 \(g\) 的 \((t,k+b)\) 状态。

    • 如果 \(a=t\),那么当 \(b\le k\) 的时候,可以转移到 \(g\) 的 \((t,k-b)\) 状态;当 \(b>k\) 的时候,会剩下一些 \(t\) 种宝石,此时可以切换回“任意选”的操作(因为这也不会出现 \(t\) 以外的宝石内部匹配的情况),于是转移到 \(f\) 的 \((t,b-k)\) 状态。

直接转移的复杂度为 \(O(n^2M^2)\),主要矛盾在 \(f\) 的 \(t\neq b\) 部分。

分类讨论,如果 \(k\le b\),那么会对 \([b-k,b+k]\) 中下标为某一奇偶性的位置和一个等差数列取较小值,因为这样的区间一定跨过 \(b\) 这个点,所以直接从这里断开并打标记。如果 \(k<b\),则会对 \([k-b,k+b]\) 中的下标做同样的操作,因为这样的区间长度固定,所以可以用单调队列维护。复杂度因而被优化为了 \(O(n^2M)\)。

代码

但是我代码常数好大啊!!!

#include <bits/stdc++.h>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int INF = 0x3f3f3f3f;
const int MAXN = 55, MAXM = 2005;

template<typename _T>
inline void Read( _T &x ) {
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) Write( x / 10 );
    putchar( x % 10 + '0' );
}

struct DPBase {
    int f[5][MAXM], g[5][MAXM];

    DPBase() {
        memset( f, 0x3f, sizeof f );
        memset( g, 0x3f, sizeof g );
    }

    inline void Clear( const int &m ) {
        rep( j, 0, 4 )
            memset( f[j], 0x3f, ( m + 1 ) << 2 ),
            memset( g[j], 0x3f, ( m + 1 ) << 2 );
    }
};

struct Queue {
    int q[MAXM], h, t;

    Queue(): q{}, h( 1 ), t( 0 ) {}

    inline bool Empty() const { return h > t; }
    inline void Clear() { h = 1, t = 0; }
    inline void Push( const int &x ) { q[++ t] = x; }
    inline  int Front() const { return q[h]; }
    inline  int Back() const { return q[t]; }
    inline void PopFront() { h ++; }
    inline void PopBack() { t --; }
};

DPBase dp[2][MAXN], tmp[2];

int cost[MAXN][5], disc[MAXN][5], pref[MAXN][5];
int suCost[5];

int N, M;

inline void ChkMin( int &x, const int &v ) {
    x = x < v ? x : v;
}

inline void operator += ( DPBase &a, const DPBase &b ) {
    rep( i, 0, 4 ) rep( j, 0, M ) ChkMin( a.f[i][j], b.f[i][j] );
    rep( i, 0, 4 ) rep( j, 0, M ) ChkMin( a.g[i][j], b.g[i][j] );
}

inline void Transfer( DPBase &tar, const DPBase &src, const int &a, const int &b ) {
    static Queue q[2];
    static int tag[MAXM] = {};
    
    // 转移 f
    rep( j, 0, M - b )
        ChkMin( tar.f[a][j + b], src.f[a][j] );
    rep( i, 0, 4 ) if( i ^ a ) {
        memset( tag, 0x3f, ( M + 1 ) << 2 );

        rep( j, 0, b ) ChkMin( tar.f[a][b - j], src.f[i][j] + j );
        rep( j, b + 1, M ) ChkMin( tar.f[i][j - b], src.f[i][j] + b );
        for( int j = 0 ; j <= b && j <= M - b ; j ++ ) {
            ChkMin( tag[b + j], src.f[i][j] );
            ChkMin( tag[b - j], src.f[i][j] + j );
        }
        per( j, M - 2, b ) ChkMin( tag[j], tag[j + 2] + 1 );
        rep( j, 2, b ) ChkMin( tag[j], tag[j - 2] - 1 );
        q[0].Clear(), q[1].Clear();
        for( int j = b + 1, o ; j <= M ; j ++ ) {
            o = ( j ^ b ) & 1;
            while( ! q[o].Empty() && q[o].Front() < j - b ) q[o].PopFront();
            while( ! q[o].Empty() && src.f[i][j] + ( ( j + b ) >> 1 ) <= src.f[i][q[o].Back()] + ( ( q[o].Back() + b ) >> 1 ) ) q[o].PopBack();
            q[o].Push( j );
            o = j & 1;
            if( ! q[o].Empty() )
                ChkMin( tag[j], src.f[i][q[o].Front()] + ( ( q[o].Front() + b - j ) >> 1 ) );
        }
        q[0].Clear(), q[1].Clear();
        for( int j = M - b, o ; j >= 0 ; j -- ) {
            o = ( j ^ b ) & 1;
            while( ! q[o].Empty() && q[o].Front() > j + b ) q[o].PopFront();
            if( j > b ) {
                while( ! q[o].Empty() && src.f[i][j] + ( ( j + b ) >> 1 ) <= src.f[i][q[o].Back()] + ( ( q[o].Back() + b ) >> 1 ) ) q[o].PopBack();
                q[o].Push( j );
            }
            o = j & 1;
            if( ! q[o].Empty() )
                ChkMin( tag[j], src.f[i][q[o].Front()] + ( ( q[o].Front() + b - j ) >> 1 ) );
        }
        rep( j, 0, 4 ) if( j ^ i && j ^ a )
            rep( k, 0, M ) ChkMin( tar.g[j][k], tag[k] );
    }
    // 转移 g
    rep( i, 0, 4 ) if( i ^ a ) rep( j, 0, M - b ) 
        ChkMin( tar.g[i][j + b], src.g[i][j] );
    rep( j, 0, b - 1 ) ChkMin( tar.f[a][b - j], src.g[a][j] + j );
    rep( j, b, M ) ChkMin( tar.g[a][j - b], src.g[a][j] + b );
}

int main() {
    Read( N );
    rep( i, 1, N ) {
        rep( j, 0, 4 ) Read( cost[i][j] ), suCost[j] += cost[i][j];
        rep( j, 0, 4 ) Read( disc[i][j] ), pref[i][j] = pref[i - 1][j] + disc[i][j];
    }
    rep( j, 0, 4 ) M = std :: max( M, suCost[j] );
    int pre = 1, nxt = 0;
    rep( j, 0, 4 ) dp[nxt][1].f[j][0] = 0;
    rep( i, 2, N + 1 ) {
        pre ^= 1, nxt ^= 1;
        rep( j, 1, i ) dp[nxt][j].Clear( M );
        rep( j, 1, i - 1 ) {
            // 转移 j
            tmp[0] = dp[pre][j];
            rep( k, 0, 4 )
                tmp[~ k & 1].Clear( M ), Transfer( tmp[~ k & 1], tmp[k & 1], k, std :: max( 0, cost[j][k] - ( pref[i - 1][k] - disc[j][k] ) ) );
            dp[nxt][i] += tmp[1];
            // 转移 i
            if( i > N ) continue;
            tmp[0] = dp[pre][j];
            rep( k, 0, 4 )
                tmp[~ k & 1].Clear( M ), Transfer( tmp[~ k & 1], tmp[k & 1], k, std :: max( 0, cost[i][k] - ( pref[i - 1][k] - disc[j][k] ) ) );
            dp[nxt][j] += tmp[1];
        }
    }
    int ans = INF;
    rep( i, 0, 4 ) {
        ChkMin( ans, dp[nxt][N + 1].f[i][0] );
        ChkMin( ans, dp[nxt][N + 1].g[i][0] );
        rep( j, 1, M )
            ChkMin( ans, dp[nxt][N + 1].f[i][j] + j );
    }
    Write( ans + N ), putchar( '\n' );
    return 0;
}

标签:tmp,宝石,rep,int,ChkMin,UOJ811,inline,璀璨
From: https://www.cnblogs.com/crashed/p/17560970.html

相关文章

  • 2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X
    2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值你在某天遇到X价值的宝石,X价值如果是所有剩余宝石价值中的最小值,你会将该宝石送人X价值如果不是所有剩余宝石价值中的最小值,你会将该宝石放到所有宝石的最后返回把宝石都送人需要多少天比如arr=[3,1,4,3,1,2]在第1......
  • 2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X
    2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值你在某天遇到X价值的宝石,X价值如果是所有剩余宝石价值中的最小值,你会将该宝石送人X价值如果不是所有剩余宝石价值中的最小值,你会将该宝石放到所有宝石的最后返回把宝石都送人需要多少天比如arr=[3,1,4,3,1,2]在第1天,你遇......
  • 2023-5-21 #55 渐行渐远迷路的我 看向了光年外璀璨星河
    358P5897[IOI2013]wombats线段树维护矩阵乘法,注意到有决策单调性,复杂度\(O(nC^2\logn)\),但是空间过大,我们递归到一个较小的区间时暴力计算即可,若阈值为\(k\),空间会整体除\(k\)。359P8275[USACO22OPEN]262144RevisitedP先考虑一个序列的问题:答案显然不超过最大值\(......
  • 贝拿勒斯圣庙的宝石针
    题目描述法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的n片金片。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金......
  • 与ChatGPT玩文字冒险游戏[寻五宝石]
    注:文中的图片来自另一个AI生成图片的程序。我:请重新开始一个文字冒险游戏。由你来描述游戏场景(盗墓情节),由我来决定采取的动作。请详细描述场景中所有的物品、生物。如果......
  • 与百度文心玩文字冒险游戏[寻五宝石]
    百度文心测试我:请开始一个文字冒险游戏。由你来描述游戏场景(盗墓情节),由我来决定采取的动作。请详细描述场景中所有的物品、生物。如果场景中的人物在对话或者跟主角对话,请......
  • 泰姬陵坐落于印度古都阿格,是十七世纪莫卧儿帝国皇帝沙杰罕为纪念其爱妃所建,她宏伟壮观
    泰姬陵坐落于印度古都阿格,是十七世纪莫卧儿帝国皇帝沙杰罕为纪念其爱妃所建,她宏伟壮观,纯白大理石砌建而成的主体建筑叫人心醉神迷,成为世界七大奇迹之一。陵寝以宝石镶饰,图......
  • 【Android开发】范例4-猜猜宝石放在哪个箱子里
    实现"猜猜宝石放在哪个箱子"的小游戏:主界面中有三个箱子,单击其中任意一个箱子,将打开箱子,显示里面是否有宝石,并且将没有被单击的箱子设为半透明显示,被......
  • 3600、宝石与石头
    给你一个字符串jewels代表石头中宝石的类型,另有一个字符串stones代表你拥有的石头。stones中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝......
  • P7518 [省选联考 2021 A/B 卷] 宝石
    非常有意义的一道题,虽然不算太难。非常好题目,英雄联盟,爱来自瓷器。戳我看题题意给一定一个\(n\)个点的树,每个点有一个颜色,点\(u\)的颜色为\(w_u\)。给定一个\(P_......