• 最优匹配问题


    题意求集合大小为偶数的两两匹配的最小权值和。(比如说定义为两点间的距离)

    设di,S 为前i个数,已选的点的集合是S的最小权值和。

    容易想到的状态转移方程为

    di,S = min{di,S , di-1,s-{i}-{j} + p[i][j]} |i,j∈{S}

    但是这样每一次转移都要枚举i,j,再加上S贡献的2n的复杂度,总复杂度为O(n2*2n),无法接受。

    但是我们发现,我们的i其实可以包含在S里面,只需要保证i是S中的最大值或者最小值即可,这个可以方便的维护。

    dS = min{dS , ds-{i}-{j} + p[i][j]} |i=max{S}, j∈{S}

    紫书上说,平均获得最大或者最小值的判断次数为2,这个怎么证明呢?

    对于最后一个1在第n位的,一共有2n状态要算,每个状态算一次,因为遍历到n的时候就可以直接break了。

    对于最后一个1在第n-1位的(此时第n位一定是0),一共有2n-1状态要算,每个状态算两次。

    ……

    所以一共算的次数是2n*1+2n-1*2+2n-2*3+……+21*n

    这个用高中的数学知识求和(隔位相减法可以求得)最高次是2n+1级别的。

    最后全部除以2n得到均摊复杂度是2。(其他次数小于2n+1的全部视为“无穷小量”)

    得证。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define db double
    
    using namespace std;
    
    const int maxn = 105, inf = 1e9;
    
    int n;
    
    db d[1 << 20 + 5];
    
    struct node
    {
        int x, y, z;
    }a[maxn];
    
    db dis(int i, int j)
    {
        return sqrt((a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y));
    }
    
    int main()
    {
        freopen("最优配对问题.in","r",stdin);
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d%", &a[i].x, &a[i].y);
        }
        
        for (int S = 0; S < (1 << n); S++)
        {
            d[S] = S == 0 ? 0 : inf;
            int j;
            for (j = n - 1; j >= 0; j--) if (S & (1 << j)) break;//如此可以保证每次选出来的j是最大的
            for (int i = 0; i < j; i++)
                if (S & (1 << i))
                    d[S] = min(d[S], dis(i + 1, j + 1) + d[S ^ (1 << (i + 1)) ^ (1 << (j + 1))]);
        }
        printf("%f", d[(1 << n) - 1]);
        return 0;
    }
  • 相关阅读:
    随堂作业——到底有几个“1”(C++)
    《你的灯亮着吗》读书笔记3
    《你的灯亮着吗》读书笔记2
    软件工程随堂小作业——寻找“水桶”(C++)
    《你的灯亮着吗》读书笔记1
    《梦断代码》阅读笔记一
    软件工程课堂练习--四则运算(三)
    软件工程课堂练习--结对初体验
    软件工程课堂练习--四则运算单元测试
    软件工程课堂练习四则运算续篇
  • 原文地址:https://www.cnblogs.com/yohanlong/p/7732698.html
Copyright © 2020-2023  润新知