UVA 1016 - Silly Sort

题目:http://uva.onlinejudge.org/external/10/1016.pdf


题意:就是将一个排列通过不断的交换两个数的位置,使得排列变成{1,2,3,4.....n}交换两个数的cost是交换的两个数的和。


思路:首先,我们能得到一个排列。然后根据置换得到一个一个的环。在每个环内,因为最小的数肯定不再自己的位置上,那么我们把那个最小数的位置应该放的数换到过来,直到最小的数在自己的位置。这样对于一个环来讲是最优的。 但是仅仅这样是不够的,我们可能会将所有数中的最小值跟这个环内的最小值换一下位置,这样就能让全部的最小值加入这个环中,然后按照之前的做法,看下哪种代价比较小。具体可以看代码。


代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <string.h>
using namespace std;
#define rep(i,a,b) for(int i = (a); i < (b); ++i)
#define rrep(i,b,a) for(int i = (b); i >= (a); --i)
#define clr(a,x) memset(a,x,sizeof(a))
#define eb emplace_back
const int maxn = 1000 + 5;
int a[maxn], p[maxn], pos[maxn];
bool vis[maxn];
vector<int> id;
int n;

int ID(int x) { return lower_bound(id.begin(),id.end(),x) - id.begin(); }
int main()
{
    #ifdef ACM
        freopen("in.txt","r",stdin);
    #endif // ACM
    int cas = 0;
    while (scanf("%d",&n)==1 && n) {
        id.clear();
        id.eb(-1);
        rep(i,1,n+1) {
            scanf("%d",a+i);
            id.eb(a[i]);
        }
        sort(id.begin(),id.end());
        id.erase(unique(id.begin(),id.end()),id.end());
        rep(i,1,n+1) {
            p[i] = ID(a[i]);
            pos[p[i]] = i;
        }
        int ans = 0;
        clr(vis,0);
        rep(i,1,n+1) if (!vis[i]) {
            int rt = i;
            vector<int> cir;
            while (!vis[rt]) {
                vis[rt] = true;
                cir.eb(rt);
                rt = p[rt];
            }
            sort(cir.begin(),cir.end());
            if (cir.size() == 1) continue;
            int min_cost = 0;
            for(auto j : cir) min_cost += id[j];
            min_cost += ((int)cir.size() - 2) * id[cir[0]];
            if (cir[0] != 1) {
                int cost = id[cir[0]] + id[1];
                cir.insert(cir.begin(),1);
                for(auto j : cir) cost += id[j];
                cost += ((int)cir.size() - 2) * id[cir[0]];
                min_cost = min(min_cost,cost);
            }
            ans += min_cost;
        }
        ++cas;
        printf("Case %d: %d\n\n",cas,ans);
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值