zoukankan      html  css  js  c++  java
  • UVA 10123 No Tipping

    UVA_10123

        以下斜体部分是我第一次写的解题报告,但后来越来越觉得我实现的程序和我的想法并不怎么挂钩,于是就越发怀疑程序里那个剪枝的正确性了。

        再后来看了UVA的论坛之后,发现上面有人提到用记忆化搜索去解,我这时再一看N只有20,完全可以用状态压缩+记忆化搜索来做,只是当时没有想到,其中一部分原因也是觉得LRJ放在了回溯这章里面,就压根没向着dp的方向去考虑,一直在想怎么剪枝去了,便也凑巧,居然诞生了下面的我现在也不知道正确与否的回溯做法。以后千万不要局限于已知的这题的归类,换个思路也许就能海阔天空了。

        如果各位兄台谁有兴趣证明出了或者推翻了下面这个回溯的程序,还望能和小弟分享一下。当然,这个回溯程序在UVA上是能AC的,而且跑得很快,但我后来总觉得这个程序的剪枝有问题。

        下面还是说一下dp的思路吧。由于N不大,2的20次方也就10^6左右,因此可以把所有木块的状态用2进制表示出来,于是可以用f[i]表示是否可以达到木块的状态为i的这种状态。接下来我们既可以一个一个地向上放,也可以一个一个地向下拿,只要看最后起始状态的f[st]是否为1即可。

        “首先,我们如果考虑怎么拿的话会比较麻烦,不如我们反过来考虑怎么放,如果每个时刻放的都能平衡,那么最后就能得出一个可行解。

        “但是如果直接回溯的话会超时,我们不妨慢慢来优化算法。

        “第一,最好把坐标先乘2化成整数,这样就避免了浮点数误差。

        “第二,我们在放的时候,一个贪心思路就是先把两个支点中间的木块放上,比较容易证明这样只会增加后续木板的稳定程度。

        “第三,我们应该把其余的木块按力矩大小降序排序,支点是离它最近的那个支点,先选力矩小的,这样一定程度上减少了木板弄翻的可能。

        “第四,上面的按力矩排序还带来了额外的好处,我们不妨用两个数组分别存左边和右边的待放的木块。我们先考虑右边的,假设我们现在放了一个木块上去,结果发现后面无论怎么放木板都会右翻,显然就不用再尝试这个木块右边的木块了,因为必然也会右翻。同样,对于左边的木块,假设我们现在放了一个木块上去,结果发现后面无论怎么放木板都会左翻,显然就不用再尝试这个木块左边的木块了,因为必然也会左翻。当然,如果后续可能左翻也可能右翻的话就要继续尝试剩余的木块。

        “考虑完上面几点之后就可以AC了。”

    //深搜剪枝,回溯
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #define MAXD 30
    int L, N, W, s[MAXD], r1[MAXD], r2[MAXD], N1, N2, x[MAXD], w[MAXD], vis[MAXD];
    int cmp(const void *_p, const void *_q)
    {
    int *p = (int *)_p;
    int *q = (int *)_q;
    int x1 , x2;
    if(x[*p] < 0)
    x1 = (-3 - x[*p]) * w[*p];
    else
    x1 = (x[*p] - 3) * w[*p];
    if(x[*q] < 0)
    x2 = (-3 - x[*q]) * w[*q];
    else
    x2 = (x[*q] - 3) * w[*q];
    return x1 - x2;
    }
    void init()
    {
    int i;
    for(i = 0; i < N; i ++)
    {
    scanf("%d%d", &x[i], &w[i]);
    x[i] *= 2;
    }
    }
    int dfs(int left, int right, int num)
    {
    int i, j, k, t, flag, mleft, mright, tleft = 1, tright = 1;
    if(num == N)
    return 2;
    for(i = 0; i < N1; i ++)
    {
    k = r1[i];
    if(!vis[k])
    {
    vis[k] = 1;
    mleft = left + (x[k] + 3) * w[k], mright = right + (3 - x[k]) * w[k];
    s[num] = k;
    if(mleft >= 0)
    tleft = 0;
    if(mleft >= 0 && mright >= 0)
    {
    flag = dfs(mleft, mright, num + 1);
    if(flag == 2)
    return 2;
    if(flag == -1)
    break;
    tleft = 0;
    }
    vis[k] = 0;
    }
    }
    for(i = 0; i < N2; i ++)
    {
    k = r2[i];
    if(!vis[k])
    {
    vis[k] = 1;
    mleft = left + (x[k] + 3) * w[k], mright = right + (3 - x[k]) * w[k];
    s[num] = k;
    if(mright >= 0)
    tright = 0;
    if(mleft >= 0 && mright >= 0)
    {
    flag = dfs(mleft, mright, num + 1);
    if(flag == 2)
    return 2;
    if(flag == 1)
    break;
    tright = 0;
    }
    vis[k] = 0;
    }
    }
    return tright - tleft;
    }
    void solve()
    {
    int i, j, k, left, right, num;
    memset(vis, 0, sizeof(vis));
    num = 0;
    left = right = 3 * W;
    for(i = 0; i < N; i ++)
    if(x[i] >= -3 && x[i] <= 3)
    {
    s[num ++] = i;
    vis[i] = 1;
    left = left + (x[i] + 3) * w[i], right = right + (3 - x[i]) * w[i];
    }
    N1 = N2 = 0;
    for(i = 0; i < N; i ++)
    if(!vis[i])
    {
    if(x[i] < 0)
    r1[N1 ++] = i;
    else
    r2[N2 ++] = i;
    }
    qsort(r1, N1, sizeof(r1[0]), cmp);
    qsort(r2, N2, sizeof(r2[0]), cmp);
    if(dfs(left, right, num) != 2)
    printf("Impossible\n");
    else
    {
    for(i = N - 1; i >= 0; i --)
    printf("%d %d\n", x[s[i]] / 2, w[s[i]]);
    }
    }
    int main()
    {
    int t = 0;
    for(;;)
    {
    scanf("%d%d%d", &L, &W, &N);
    if(!L)
    break;
    init();
    printf("Case %d:\n", ++ t);
    solve();
    }
    return 0;
    }
    //状态压缩+记忆化搜索
    #include<stdio.h>
    #include<string.h>
    #define MAXD 1100000
    #define MAXN 25
    int N, L, W, x[MAXN], w[MAXN];
    int f[MAXD], left[MAXD], right[MAXD];
    void init()
    {
    int i;
    for(i = 0; i < N; i ++)
    {
    scanf("%d%d", &x[i], &w[i]);
    x[i] *= 2;
    }
    }
    int dp(int st)
    {
    int i, k, mleft, mright;
    if(f[st] != -1)
    return f[st];
    if(st == 0)
    return f[st] = 1;
    for(i = 0; i < N; i ++)
    if((1 << i) & st)
    {
    mleft = left[st] + (x[i] + 3) * w[i];
    mright = right[st] + (3 - x[i]) * w[i];
    if(mleft >= 0 && mright >= 0)
    {
    k = st ^ (1 << i);
    left[k] = mleft, right[k] = mright;
    if(dp(k))
    {
    printf("%d %d\n", x[i] / 2, w[i]);
    return f[st] = 1;
    }
    }
    }
    return f[st] = 0;
    }
    void solve()
    {
    int i, st;
    memset(f, -1, sizeof(f));
    st = (1 << N) - 1;
    left[st] = W * 3, right[st] = W * 3;
    if(!dp(st))
    printf("Impossible\n");
    }
    int main()
    {
    int t = 0;
    for(;;)
    {
    scanf("%d%d%d", &L, &W, &N);
    if(!L)
    break;
    init();
    printf("Case %d:\n", ++ t);
    solve();
    }
    return 0;
    }
  • 相关阅读:
    fiddler居然有mac版本了
    java学习笔记02 导入,方法调用,私有公有,静态非静态
    apscheduler笔记
    java学习笔记01 类型,List,Set,循环
    fiddler笔记
    为什么有些端口不能用?
    ubuntu借网
    filecoin
    django密码生成
    python-panda
  • 原文地址:https://www.cnblogs.com/staginner/p/2295362.html
Copyright © 2011-2022 走看看