zoukankan      html  css  js  c++  java
  • 最小成本排序

    Aizu ALDS1_6_D   Minimum Cost Sort

    直接排序太简单了,我们来考虑一下排序的代价,对于一个长度n的由非负整数组成的数组,我们用交换法排序,每次可以交换任意两个元素,而交换的代价,是这两个元素之和,求出排序的最少交换代价,保证数组里没有两个元素相同且元素值小于等于10000

    Input

    第一行输入n,第二行输入n个非负整数,1<n<=1000

    Output

    输出排序的最小代价

    Sample Input 1

    5
    1 5 3 4 2
    

    Sample Output 1

    7
    

    思路:

      使用成环交换排序取最小代价

    方法:

      我们以W = {4, 3, 2, 7, 1, 6, 5}为例进行分析。现在的目标是求出将W重排为{1, 2, 3, 4, 5, 6, 7}时所需的最小成本。我们不妨先画出一张标出每个元素最终将移动到哪个位置的草图。(如图1所示)

     图中有3个闭合的圆,分别是4 - 7 - 5 - 1 - 4;3 - 2 - 3;6 - 6。现在我们来分析每个圆所需的最小成本。

     为自环即1长度的圆无需移动,成本为0。

     长度为2交换位置即可,成本为二者之和。如3 - 2 - 3的成本为3 + 2 = 5。

     对于长度大于等于3的圆。(如图2所示)在处理4 - 7 - 5 - 1 - 4时,通过1来移动其他元素可以保证成本最小。

    圆中的元素为wi,圆内元素数为n,那么此时的成本为Σwi+(n−2)∗min(wi)Sigma wi+ (n-2)*min(wi)Σwi+(n−2)∗min(wi)

    由于各元素至少移动一次,所以有ΣwiSigma wiΣwi。再加上最小值在最后一次交换前要移动(n - 2)次,所以有(n - 2) * min(wi)。上式在n = 2时也成立。

    于是最小成本为(5 + 0) + (17 + 2) = 24。

    这个方法乍眼一看像那么回事,但是你仔细一寻思发现还存在反例。(如图3所示)

    如果套用之前的方法,1 - 2 - 1成本为3,8 - 10 - 9 - 7 - 8成本为48,总成本为51。

    但若先将7和1交换,把圆8 - 10 - 9 - 7 - 8改为8 - 10 - 9 - 1 - 8,这部分的成本就成了28 + 2 * 1 = 30。然后再加上两次交换7和1以及圆1 - 2 - 1的成本,总成本为49。可见,即便我们多加了两次1和7交换的成本,总成本依然小于之前的方法,显然有时从圆外借元素来移动,能让成本更低。

    设外圆的元素为x。借元素增加的成本为2∗(min(wi)+x)2*(min(wi)+x)2∗(min(wi)+x),节约的成本为(n−1)∗(min(wi)−x)(n-1)*(min(wi)-x)(n−1)∗(min(wi)−x)此时该部分的总成本为

    Σwi+(n−2)∗min(wi)+2∗(min(wi)+x)−(n−1)∗(min(wi)−x)=Σwi+min(wi)+(n+1)∗xSigma wi+(n-2)*min(wi)+2*(min(wi)+x)-(n-1)*(min(wi)-x)=Sigma wi+min(wi)+(n+1)*xΣwi+(n−2)∗min(wi)+2∗(min(wi)+x)−(n−1)∗(min(wi)−x)=Σwi+min(wi)+(n+1)∗x
    可见x应选用整个输入中最小的元素。

    综上,程序需要计算“借整体最小元素”与“不借元素”的两种情况,选出其中成本较小的一方。

    AC代码:

    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define DOF 0x7f7f7f7f
    #define mem(a,b) memset(a,b,sizeof(a))
    #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    typedef long long ll;
    using namespace std;
    const int maxn=1e5+10;
    int n;
    int a[maxn],b[maxn],vis[maxn];
    ll solve()
    {
        map<int,int>mt;
        int x=INF;
        for(int i=1;i<=n;++i)
        {
            b[i]=a[i];
            x=min(x,a[i]);
        }
        sort(b+1,b+1+n);
        for(int i=1;i<=n;++i)
            mt[b[i]]=i;
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            ll cnt=0,sum=0,minn=INF,tmp=i;
            while(1)
            {
                vis[tmp]=1;
                ++cnt;
                minn=min(minn,a[tmp]*1ll);
                sum+=a[tmp];
                tmp=mt[a[tmp]];
                if(vis[tmp]) break;
    
    
            }
            ans=ans+min(sum+(cnt-2)*minn,sum+(cnt-2)*minn+2*(minn+x)-(cnt-1)*(minn-x));
        }
        return ans;
    }
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;++i) cin>>a[i];
        ll ans=solve();
        cout<<ans<<endl;
    }

    模板:

    const int maxn=1e5+10;
    int n;
    int a[maxn],b[maxn],vis[maxn];
    ll solve()
    {
        map<int,int>mt;
        int x=INF;
        for(int i=1;i<=n;++i)
        {
            b[i]=a[i];
            x=min(x,a[i]);
        }
        sort(b+1,b+1+n);
        for(int i=1;i<=n;++i)
            mt[b[i]]=i;
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            ll cnt=0,sum=0,minn=INF,tmp=i;
            while(1)
            {
                vis[tmp]=1;
                ++cnt;
                minn=min(minn,a[tmp]*1ll);
                sum+=a[tmp];
                tmp=mt[a[tmp]];
                if(vis[tmp]) break;
    
    
            }
            ans=ans+min(sum+(cnt-2)*minn,sum+(cnt-2)*minn+2*(minn+x)-(cnt-1)*(minn-x));
        }
        return ans;
    }
    const int maxn=1e5+10;
    int n;
    int a[maxn],b[maxn],vis[maxn];
    ll solve()
    {
        map<int,int>mt;//用来存环的序号
        int x=INF;
        for(int i=1;i<=n;++i)
        {
            b[i]=a[i];
            x=min(x,a[i]);//全局最小值
        }
        sort(b+1,b+1+n);
        for(int i=1;i<=n;++i)
            mt[b[i]]=i;//取每个元素的序号
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            ll cnt=0,sum=0,minn=INF,tmp=i;
            while(1)//成环判断
            {
                vis[tmp]=1;
                ++cnt;
                minn=min(minn,a[tmp]*1ll);
                sum+=a[tmp];
                tmp=mt[a[tmp]];
                if(vis[tmp]) break;
    
    
            }
            ans=ans+min(sum+(cnt-2)*minn,sum+(cnt-2)*minn+2*(minn+x)-(cnt-1)*(minn-x));//取两种的最小代价
        }
        return ans;
    }

    原文地址:https://blog.csdn.net/qq_40998706/article/details/86663101?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

  • 相关阅读:
    全排列
    合并两个有序列表——递归&非递归
    学习笔记:oracle学习一:oracle11g体系结构之体系结构概述和逻辑存储结构
    学习笔记:oracle之win10安装卸载oracle 11gR2步骤及常见问题解决
    日常工作问题解决:配置NTP服务器以及一些常见错误解决
    日常工作问题解决:redhat6.9--解决yum功能不能正常使用和配置yum源
    日常工作问题解决:Redhat6.5--解决yum无法正常安装配置问题
    日常工作问题解决:使用vmvare克隆centos6虚拟机造成无eth0的解决办法
    日常工作问题解决:centos7下配置网卡以及查询网卡UUID
    日常工作问题解决:centos7下使用yum安装软件报yum.pid锁定
  • 原文地址:https://www.cnblogs.com/waryan/p/12628202.html
Copyright © 2011-2022 走看看