ACM: polya定理+hash 数论题 poj 3…

 

                                                              Cow Sorting

Description

Farmer John's N (1 ≤ N ≤ 10,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" level in the range 1...100,000. Since grumpy cows are more likely to damage FJ's milking equipment, FJ would like to reorder the cows in line so they are lined up in increasing order of grumpiness. During this process, the places of any two cows (not necessarily adjacent) can be interchanged. Since grumpy cows are harder to move, it takes FJ a total of X+Y units of time to exchange two cows whose grumpiness levels are X and Y.

Please help FJ calculate the minimal time required to reorder the cows.

Input

Line 1: A single integer: N.
Lines 2..N+1: Each line contains a single integer: line i+1 describes the grumpiness of cow i.

Output

Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.

Sample Input

3

2 3 1

Sample Output

7

题意: 有N头奶牛,现在你要将这全部奶牛按照升序排序,但是每次两头奶牛交换是有权值的X+Y.

           要求权值最小.

解题思路:

                 1. 先将题目模型抽象出来, 一组数组从一个状态变成另外一个状态,即是一个置换完成了.

                  根据群论知识,置换可以分成不同的并且不相交的循环的乘积.  (离散数学知识).

                  《算法艺术与信息学竞赛》  ====>>>>>

                例如: 初始序列: 8 4 5 3 2 7

                         最终序列: 2 3 4 5 7 8

                循环有: (8 2 7) (4 3 5)           (两个序列像锯齿状看成8->2 , 2 -> 7 , 7 -> 8)

                各个循环是相互独立的,所以应该依次完成每个循环.

                 (1).任意的一个循环i , 设它的长度为Ki , 容易证明至少需要交换Ki-1次 , 即每次让一个元素到达目标

               位置 , 而当第Ki-1个元素到达目标以后显然第Ki个已经达到目标了. 既然交换次数是一定的 , 应该让

               每次交换的代价尽量少. 显然每个元素至少要交换一次, 因此一个比较好的方法是让循环中的最小元

               ti素参加所有的交换, 其他元素只各参加一次, 总花费为sumi + (Ki-2)*ti , 其中sumi为循环i中所有数和.

                 (2). 不过上诉结果是否是最优解. 我们可以借助循环外的元素和它们交换.

                就是让ti先和所有n个数中最小值m交换, 让m进入循环, 并和剩下的Ki-1个元素各交换一次 , 把它们送

                入目标位置, 最后让ti和m交换 ,  退出该循环. 显然第二种方法的花费: sumi + ti + (Ki+1)*m , 它有可能

                是比第一种方法优.

                例如: 1 8 9 7 6   ===>> 1 6 7 8 9   

                循环: (1) , (8 6 9 7)  (第一个循环只有一个元素忽略.)

                第一种花费: 6 + 7 + 8 + 9 + (4-2)*6 = 42

                第二种花费: 6 + 7 + 8 + 9 + 6 + (4+1)*1 = 41

                综上所述:  tatal = sum + ∑ min{   (Ki-2)*ti     ti + (Ki+1)*m  };

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define MAX 100005

int n;
int a[MAX],b[MAX];
int next[MAX];
bool vis[MAX];

inline int minsize(int a,int b)
{
    return a > b ? b : a;
}

int polya()
{
    int sum = 0;
    int min = b[1];
    int t_min = 0;
    memset(vis,false,sizeof(vis));

    for(int i = 1; i <= n; ++i)
    {
        if(vis[i])
            continue;
        int j = i;
        t_min = 1 << 20;
        int num = 0;
        int first = a[j];
        while(true)
        {
            vis[j] = true;
            sum += a[j];
            if(t_min > a[j])
                t_min = a[j];
            num++;
            if(b[j] == first)
                break;
            j = next[b[j]];
        }
        //cout << "t_min " << t_min << endl;
        //cout << "min " << min << endl;
        sum += minsize(t_min*(num-2),t_min+(num+1)*min);
    }
    return sum;
}

int main()
{
//    freopen("input.txt","r",stdin);
    while(scanf("%d",&n) != EOF)
    {
        memset(next,0,sizeof(next));
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d",&b[i]);
            next[b[i]] = i;
            a[i] = b[i];
        }
        sort(b+1,b+n+1);
        
        printf("%d\n",polya());
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值