题解 P1433 【吃奶酪】

这道题是一道著名的NP问题。

正解应该是DP,但我在这里讲一种近似算法——爬山。

希望某些dalao注意一下爬山与模拟退火的区别。

爬山是直往低处往高处爬,每次取大的,也就是一种贪心思想。

而模拟退火则是概率性接受不优解。

不过一次爬山不一定可以找出最优解,要多次随机。

贪心思路如下:

  1. 随机数组
  2. for i=1->n,j=1->n 依次枚举两点
  3. 如果交换i,j后大小减小,更新答案
  4. 回到1操作,并执行M次。

其中M是可选的,与your score成正比关系(当然太大就T了)

注意2,3不能保证已经无法更新(交换两个使答案减少),尽量做两次2,3。

我取M=50就A了,并是本题最快算法(我上面的都是面♂向♂数♂据♂编♂程

代码如下:

#include<bits/stdc++.h>
#define repeat(a,b,c,g) for (int a=b,abck=(g>=0?1:-1);abck*(a)<=abck*(c);a+=g)
using namespace std;
struct point {
    double x,y;
}a[20];
int n;
double operator - (point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double calc()
{
    double ans=0;
    repeat(i,1,n,1)
    ans += a[i-1] - a[i];
    return ans;
}
int main()
{
    srand(1);
    cin >> n;
    a[0].x = 0;
    a[0].y = 0;
    repeat(i,1,n,1)
    {
    	cin >> a[i].x >> a[i].y;
    }
    double MIN = calc();
    repeat(_,1,50,1)
    {
        repeat(i,1,n,1)
        {
            swap(a[rand()%n+1],a[i]);
        }
        repeat(i,1,n,1)
            repeat(j,i+1,n,1)
            {
                double tmp = calc();
                MIN = min(tmp,MIN);
                swap(a[i],a[j]);
                double tp2 = calc();
                if (tp2 > tmp)
                    swap(a[i],a[j]);
                MIN = min(tp2,MIN);
            }
        repeat(i,1,n,1)
            repeat(j,i+1,n,1)
            {
                double tmp = calc();
                MIN = min(tmp,MIN);
                swap(a[i],a[j]);
                double tp2 = calc();
                if (tp2 > tmp)
                    swap(a[i],a[j]);
                MIN = min(tp2,MIN);
            }
    }
    printf("%.2f",MIN);
}

其实还可以更优,改过后不用再O(n)统计,直接O(1)计算

复杂度O(n*n*m)

posted @ 2019-07-13 10:27  dgklr  阅读(233)  评论(0编辑  收藏  举报