uva 1016 - Silly Sort(置换的灵活应用)

本文详细解析了一种算法,用于计算将任意给定序列变为严格单调递增序列所需的最小花费。该算法通过识别并处理序列中的周期性变化,实现了最优解的快速计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你n个不同数,可以进行一种操作,交换任意两个数的位置,则花费为两数的和,求把原序列变为严格单调递增的序列的最小花费


解析:通过置换可以发现原序列可以分为多个周期(循环)的变换,如8 4 5 3 2 7 变为(8 2 7)和(4 3 5),对于每个周期,所有数都不在他升序后的位置,即所有的数都需要交换,每次用周期内最小的数与另一个数交换,设这个周期的最小值为min,数量为size,所有数的和为sum ,则易得最小花费为min*(size-1)+sum-min,这样得到的花费就是最小值了

但需注意的是在进行变换时,我们可以先把 所有数的最小值Min 和min先交换,当这个周期成升序后再交换回来,则花费为2*(min+Min)+sum-min+(size-1)*Min  比较两者最小值即答案


//保存周期

#include<iostream>
#include<cstdio>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;

#define N 10005
int k,n,a[N],c[N],b[N],vis[N],minx;
long long res;
vector<int> w[N];

void inint()
{
    int i,j;
    minx=N*2;
    for(i=0;i<N;i++)
     w[i].clear();
    for(i=0; i<n; i++)
    {
        scanf("%d",&a[i]);
        c[i]=a[i];
    }
    sort(c,c+n);
    minx=c[0];
    for(i=n-1; i>=0; i--)
    {
        b[c[i]]=a[i];
    }
    memset(vis,0,sizeof(vis));
    for(i=0; i<n; i++)
    {
        j=c[i];
        while(!vis[j])
        {
            vis[j]=1;
            w[k].push_back(j);
            j=b[j];
        }
        k++;
    }


}
int main()
{
   // freopen("in.txt","r",stdin);
   // freopen("outw.txt","w",stdout);
    int i,j,s,min,qq=0,t=0;
    while(~scanf("%d",&n)&&n)
    {
        res=k=0;
        inint();
        for(i=0; i<k; i++)
        {
           s=w[i].size();
           if(s<=1) continue;
           min=2*N;
           for(j=0;j<s;j++)
           {
             res+=w[i][j];
             if(w[i][j]<min) min=w[i][j];
           }
            res-=min;
           if(min!=minx&&min*(s-1)>(s+1)*minx+2*min)
            res+=(s+1)*minx+2*min;
           else res+=(s-1)*min;
        }
        t++;
       // if(t>=2) puts("");
        printf("Case %d: %lld\n\n",t,res);
    }
    return 0;
}

//直接求答案 快些

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>

using namespace std;

int a[100000];
int b[100000];
bool vis[100000];

int main (void)
{
    // freopen("in.txt","r",stdin);
   // freopen("out.txt","w",stdout);
    int N;
    int t = 0;
    while (~scanf("%d", &N) && N)
    {
        t++;
        for (int i = 0; i < N; ++i)
        {
            scanf("%d", &a[i]);
            b[i] = a[i];
            vis[i] = false;
        }
        sort(b, b + N);
        // Map the numbers to their desired place after sort
        map<int, int> place;
        for (int i = 0; i < N; ++i)
        {
            place[b[i]] = i;
        }

        int res = 0;
        for (int i = 0; i < N; ++i)
        {
            if (vis[i] == false)
            {
                if (place[a[i]] == i)
                {
                    vis[i] = true;
                    continue;
                }
                // We're in new cycle
                int min_val = a[i];
                int num = 0;
                int sum = 0;
                int j = i;
                while (vis[j] == false)
                {
                    sum += a[j];
                    num++;
                    if (a[j] < min_val)
                    {
                        min_val = a[j];
                    }
                    vis[j] = true;
                    j = place[a[j]];
                }
                sum -= min_val;
                res += sum ;
                // Let's try to borrow the minimum value.
                // If it's less costly then update our result.
                if (2 * (b[0] + min_val) <(min_val - b[0]) * (num - 1))
                {
                    res += ( b[0]) * (num - 1) +2 * (b[0] + min_val);
                }
                else res+=(num-1)*min_val;
            }
        }
        printf("Case %d: %d\n\n", t, res);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值