[POJ3270]Cow Sorting

题目大意:给定一个序列,每次可以选择两个元素a和b交换,代价为a+b,求将其变为升序的最小代价。

 

解题思路:将序列表示为循环相乘的形式,假设a的目标位置为x,x下的数为b,那么a和b必定在同一个循环内。容易想到将各个循环分别求解,用循环内最小的数作为媒介。但注意到两个循环A和B,将它们乘上对换(a,b),其中a属于A,b属于B,A和B将合成一个大循环C,而C内的最小的数比A或者B内的最小的数要小,将C整体求解加上合并代价a+b可能比A和B分别求解要优。

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
using namespace std;

vector<int> tb[10012];

int n, a[10010], b[10010], g[10010], f[100010], M[10010], tot[10010];
bool vis[10010];

int main()
{
    while (scanf("%d", &n) != EOF)
    {
        int i;
        for (i = 1; i <= 10010; i++) tb[i].clear();
        for (i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            b[i] = a[i];
        }
        sort(b + 1, b + n + 1);
        for (i = 1; i <= n; i++)
        {
            f[b[i]] = i;
            g[i] = b[i];
        }

        int cnt = 0;
        memset(M, 0x3f, sizeof(M));
        memset(vis, false, sizeof(vis));
        memset(tot, 0, sizeof(tot));
        for (i = 1; i <= n; i++)
        {
            if (vis[i]) continue;
            int j = i;
            cnt++;
            while (!vis[j])
            {
                vis[j] = true;
                tb[cnt].push_back(g[j]);
                tot[cnt] += g[j];
                if (g[j] < M[cnt]) M[cnt] = g[j];
                j = f[a[j]];
            }
        }

        /*for (i = 1; i <= cnt; i++)
        {

            for (int j = 0; j < tb[i].size(); j++) cout << tb[i][j] << " ";
            cout << endl;
        }*/

        int ans = 0;
        for (i = 1; i <= cnt; i++)
        {
            if (tb[i].size() == 1) continue;
            int cost1 = M[i] * (tb[i].size() - 2) + tot[i];
            int cost2 = M[i] + b[1] + b[1] * tb[i].size() + tot[i];
            //cout << cost1 << " " << cost2 << endl;
            ans += min(cost1, cost2);
        }
        cout << ans << endl;
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值