SLO-Elephants(置换 最小代价交换至目标顺序)

原题 : POI2009

题意 :

对于一个1-N的数组,每次你可以交换两个数ax与ay,代价为W(ax)+W(ay)。请问将(ai)变为(目标顺序bi)所需的最小代价是多少。

解析 :

将交换用有向线段连起来,得到的会是多个环,我们只要对这些环进行考虑即可

  1. 自环就不说了,无需考虑
  2. 如果是两个节点的环,那么直接交换即可
  3. 如果两个节点以上(设环节点数为n),就要考虑实际的交换问题了,因为每次交换必须使一个点到预期位置才能最小化代价,所以我们需要用代价最小的那个点作为媒介去交换其他点
    1. 若用环内的点作为媒介,那么应该是最小代价点n-1次,其他点1次
    2. 如果用环外的点,那么首先会选全局的最小点,用环内的最小点交换进来,在环内排序成功后再交换出去,所以,环内最小点2次,环外最小点n+1次,环内其他点1次

那么代码思路就是先判断环的大小,如果是2以上,那么先处理出所有点一次的代价,然后在(环内最小点n-2次,环内最小点1次+环外最小点n+1次)里选个小的就可以了

当然,不用考虑全局最小点在环内还是环外,因为在比较的时候一定会选最优的

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
#define LL long long
const int MAXN=1e6+9;

LL v[MAXN];
int a[MAXN];//当前数组
int to[MAXN];
bool vis[MAXN];

int main(){
    int n;
    LL minn=1e9;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&v[i]),minn=min(minn,v[i]);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){int tmp;scanf("%d",&tmp);to[tmp]=i;}
    LL ans=0;
    for(int i=1;i<=n;i++){//遍历位置
        int now=a[i];LL mi=v[now];
        if(vis[now])continue;

        int loop=1;
        vis[now]=1;//标记大象
        ans+=v[now];

        while(1){
            now=to[now];
            now=a[now];
            if(now==a[i])break;

            loop++;
            vis[now]=1;
            mi=min(mi,v[now]);
            ans+=v[now];
        }
        if(loop==1){
            ans-=v[a[i]];
        }
        else if(loop==2){
            ;
        }
        else{
            ans+=min(mi*(loop-2),mi+minn*(loop+1));
        }
    }
    printf("%lld\n",ans);
}


猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/82390974