• UVA 11125 Arrange Some Marbles


    UVA_11125

        一开始被这个题目的3000组数据吓到了,但后来真正仔细分析之后,才发现复杂度主要不是取决于这3000组数据,因此3000这个数只是唬人的。

        看到题目之后,我们发现放石子可以看成一个阶段一个阶段地去放,每次放一组即可,只不过放的时候有些条件限制,那么我们可不可以分阶段进行dp计数呢?

        带着这个疑问我们继续分析题目,发现取石子的过程并没有明确的先后顺序,所以我们不能人为规定一个取的顺序,因此每次取一组石子的这个过程只能用枚举去实现。为了能够枚举所取的石子,我们必然在每个阶段都要得知每类还剩多少石子,这样我们就不妨把剩余的每类石子的数量作为一个状态,但这个状态好像比较麻烦,但没关系,仔细想一下就会发现由于石子数比较少,我们可以把这个状态压缩成一个8进制的数,这样最多有8*8*8*8个状态嘛,很少的。

        状态已经初步表示完了,但我们发现有个问题没有解决,我们怎么让取的时候避免和前面一组同颜色或者同数量呢?再把状态细化呗,多加两个整数pc、pn吧,一个记录前面石子的颜色,一个记录前面那组石子的数量,这样就好了嘛。但第一组石子呢?也就是说我们最初的时候把这个pc和pn初始化成什么呢?等等,好像还有一个问题我们也没解决,题目还要求首和尾也不能同颜色同数量呀。

        但依然没关系,我们就把这两个问题一起解决。保证首尾不同确实不太好处理,但凡是不好处理的我们就偷懒嘛,怎么偷懒?枚举呗。如果我们指定了首或者尾那一组的状态,问题就都解决了。我们不妨指定尾的状态,颜色记为fc,数量记为fn,把两个整数也压缩进dp的状态吧。这样我们也就自然知道pc和pn一开始应该初始化成fc和fn了,于是只要后面取的过程保证不和pc同颜色,且不和pn同数量,自然就保证了首尾也是不同颜色、不同数量的。

        现在似乎没什么太多的问题了,剩下的就是看看复杂度能不能承受了,如果不能我们就得想办法优化dp。总的状态4*3*4*3*8*8*8*8,每个状态最多转移4*3次,一共是7077888,时间是没问题的。

        哦对,还有那3000组数据,其实我们刚才算的复杂度是包含了所有的状态的,只要我们一直是保持记忆化搜索的状态的话,数据的组数是没关系的,我们依旧不会重复去算一些状态,只不过增加了查询的次数而已。

        空间上为了节省一点,前面4个状态我们用4进制,后面4个状态我们用8进制,把8个数压缩成一个整数。

    #include<stdio.h>
    #include<string.h>
    #define MAXD 1100000
    int f[MAXD], a[5], b[5];
    int dp(int state)
    {
    int i, j, k, t, st = state, ans = 0, fc, fn, pc, pn;
    if(f[state] != -1)
    return f[state];
    fc = st % 4, st /= 4;
    fn = st % 4, st /= 4;
    pc = st % 4, st /= 4;
    pn = st % 4, st /= 4;
    k = 0;
    for(i = 0; i < 4; i ++)
    {
    b[i] = st % 8, st /= 8;
    k += b[i];
    }
    if(k == 0)
    {
    if(pc == fc && pn == fn)
    return f[state] = 1;
    else
    return f[state] = 0;
    }
    for(i = 0; i < 4; i ++)
    {
    if(i == pc)
    continue;
    for(j = 1; j <= 3 && j <= b[i]; j ++)
    {
    if(j == pn)
    continue;
    b[i] -= j;
    for(k = 3, t = 0; k >= 0; k --)
    t = t * 8 + b[k];
    t = (((t * 4 + j) * 4 + i) * 4 + fn) * 4 + fc;
    ans += dp(t);
    b[i] += j;
    }
    }
    return f[state] = ans;
    }
    void solve()
    {
    int i, j, k, n, res, t;
    memset(a, 0, sizeof(a));
    scanf("%d", &n);
    k = 0;
    for(i = 0; i < n; i ++)
    {
    scanf("%d", &a[i]);
    k += a[i];
    }
    if(!k)
    {
    printf("1\n");
    return ;
    }
    for(i = 3, k = 0; i >= 0; i --)
    k = k * 8 + a[i];
    res = 0;
    for(i = 0; i < n; i ++)
    for(j = 1; j <= 3 && j <= a[i]; j ++)
    {
    t = (((k * 4 + j) * 4 + i) * 4 + j) * 4 + i;
    res += dp(t);
    }
    printf("%d\n", res);
    }
    int main()
    {
    int t;
    memset(f, -1, sizeof(f));
    scanf("%d", &t);
    while(t --)
    solve();
    return 0;
    }


  • 相关阅读:
    PHP ftp_nb_continue() 函数
    PHP ftp_mkdir() 函数
    PHP ftp_mdtm() 函数
    普通索引和唯一索引,应该怎么选择
    [学习笔记]拉格朗日中值定理
    asp dotnet core 通过图片统计 csdn 用户访问
    WPF 使用 SharpDx 异步渲染
    WPF 使用 SharpDx 异步渲染
    win10 uwp 解决 SerialDevice.FromIdAsync 返回空
    win10 uwp 解决 SerialDevice.FromIdAsync 返回空
  • 原文地址:https://www.cnblogs.com/staginner/p/2292064.html
Copyright © 2020-2023  润新知