“科林明伦杯”哈尔滨理工大学第七届程序设计团队赛 G.Sorting(贪心,思路)

Description

You have an integer sequence a1 … an (integers are distinct) and you
need to sort it by ascending order. However, you can only swap two
integers at a time and the cost is (ai + aj) if you swap ai and aj.

Your task is to calculate the minimum cost of sorting.

Input

There is an integer T on the first line, then T cases follow.

For each case, there is an integer n on the first line, and n integers
a1 … an on the second line.

1 ≤ T ≤ 100

1 ≤ n ≤ 100000 (and 1 ≤ ∑n ≤ 1000000 for all test cases)

1 ≤ ai ≤ 109

Output

For each case, print the minimum cost on a single line.

Sample Input

5
3
3 2 1
4
8 1 2 4
5
1 8 9 7 6
6
8 4 5 3 2 7
9
901343232 439583812 852399982 732257750 739673755 979883931 445261874 867592420 875627527

Sample Output

4
17
41
34
9324217281

思路

先说题意,给你一个序列,你可以任意交换两个数,每次交换两个数所花费的代价是这两个数的和,问最少需要花费多少代价可以使这个序列有序。

我继续转化成图论的方式来理解一下

我们以一组样例来说明:

5
1 8 9 7 6

首先对这个序列进行排序

下标12345
排序前18976
排序后16789

我们从元素1开始看,排序后元素1的位置还是1,那么就给1到1之间连一条边,形成一个自环;再到元素8,元素8排序以后到了第4个位置,而第四个位置是元素7,所以给8到7之间连一条有向边,同理连完剩下的边可以得到一张图:
这里写图片描述

那么我们可以发现两个环,那么我们回到题目中来,要使最后的总和最小,我们的贪心思路是什么?
一:
对于每一个环的贪心思路就是,找到这个环中最小的那个点,也就是6,然后从6开始进行交换,6和9交换,可以使9到对应的位置,花费为6+9=15,然后6和7交换,花费为6+7=13,最后等到交换完毕,自最后的答案是什么呢?就是:

(6+9)+(6+7)+(6+8)=(6+7+8+9)+62=30+12=42

剩下一个环不用交换,那么当前的最小值就是42,但是这还不是最优解

二:
我们考虑,在这个图中找到一个最小的值,然后用这个值跟着当前的环进行交换,在这个图中很明显是1,我们让第1和第二个环中的最小值6进行交换,然后再像上面一样,交换1和9,花费为:1+9=10,交换1和7,花费为:1+7=8等到交换完毕,最后的结果是:

(1+6)+(1+9)+(1+7)+(1+8)+(1+6)=(6+8+7+9)+15+6=41

所以41比42小,显然41更优,所以我们的贪心策略就是在这两者之间,找出一个最小值

所以先用结构体存一下下标和值,然后用一个变量least记录一下整个序列中最小的,然后对于每个环,选择是用当前环中的最小值交换还是,整个序列的最小值进行交换,x存储当前环中点的个数,minn存储当前环中最小值,num算出当前环中的总和,最后求出这两种情况中最小的即可

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const ll N=100000+20;
struct node
{
    ll pos;
    ll val;
} a[N];
bool cmp(node a,node b)
{
    return a.val<b.val;
}
ll vis[N];
ll least;
ll solve(ll i)
{
    ll j=a[i].pos;
    ll minn=a[i].val;
    vis[i]=1;
    ll x=0,num=a[i].val;
    while(!vis[j])
    {
        num+=a[j].val;
        minn=min(minn,a[j].val);
        vis[j]=1;
        x++;
        j=a[j].pos;
    }
    return num+min(minn*(x-1),least*(x+2)+minn);
}
int main()
{
    ll n,x,t;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        mem(vis,0);
        for(ll i=1; i<=n; i++)
        {
            scanf("%lld",&a[i].val);
            a[i].pos=i;
        }
        sort(a+1,a+n+1,cmp);
        least=a[1].val;
        ll ans=0;
        for(ll i=1; i<=n; i++)
        {
            if(!vis[i])
            {
                ans+=solve(i);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值