Problem J: [POI2009]SLO
Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 622 Solved: 302
[Submit][Status][Discuss]
Description
对于一个1-N的排列(ai),每次你可以交换两个数ax与ay(x<>y),代价为W(ax)+W(ay) 若干次交换的代价为每次交换的代价之和。请问将(ai)变为(bi)所需的最小代价是多少。
Input
第一行N。第二行N个数表示wi。第三行N个数表示ai。第四行N个数表示bi。 2<=n<=1000000 100<=wi<=6500 1<=ai,bi<=n ai各不相等,bi各不相等 (ai)<>(bi) 样例中依次交换数字(2,5)(3,4)(1,5)
Output
一个数,最小代价。
Sample Input
6
2400 2000 1200 2400 1600 4000
1 4 5 3 6 2
5 3 2 4 6 1
2400 2000 1200 2400 1600 4000
1 4 5 3 6 2
5 3 2 4 6 1
Sample Output
11200
HINT
题解:想到置换,发现在一个循环中,我们尽量让每个点与权值小的进行交换,但是这样会是最优吗?
显然不是,我们忽略一种情况,我们可以将另一个循环中的一个最小的值与一个循环的一个节点交换,
然后重复上述操作,再将它换回原来循环来产生更优的解!
细节见代码:
BZOJ1119
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ll long long
#define inf 0x7fffffff
#define N 1000010
using namespace std;
int n;
ll v[N],minn=inf,a[N],ans;
int cnt[N];
bool vis[N];
ll read()
{
ll x=,f=; char ch;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') f=-;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
return x*f;
}
int main()
{
n=read();
for (int i=; i<=n; i++) v[i]=read(),minn=min(minn,v[i]);
for (int i=; i<=n; i++) a[i]=read();
for (int i=; i<=n; i++) cnt[read()]=i;
for (int i=; i<=n; i++)
{
if (!vis[i])
{
ll t=,mn=inf,sum=; int j=i;
while (!vis[j])
{
vis[j]=; t++; sum+=v[a[j]]; mn=min(mn,v[a[j]]); j=cnt[a[j]];
}
if (t>=)
{
ll t1=sum+1ll*mn*(t-),t2=sum+mn+1ll*minn*(t+);
ans+=min(t1,t2);
}
}
}
printf("%lld\n",ans);
return ;
}
BZOJ1697
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#define ll long long
#define inf 0x7fffffff
#define N 100005
using namespace std;
struct point{
ll v,pos;
}tmp[N];
ll v[N],minn=inf,a[N],ans;
int cnt[N];
bool vis[N];
ll read()
{
ll x=,f=; char ch;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') f=-;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
return x*f;
}
bool cmp(point a,point b){return a.v<b.v;
}
int main()
{
int n=read();
for (int i=; i<=n; i++) v[i]=tmp[i].v=read(),a[i]=tmp[i].pos=i,minn=min(minn,tmp[i].v);
sort(tmp+,tmp+n+,cmp);
for (int i=; i<=n; i++){cnt[tmp[i].pos]=i;}
for (int i=; i<=n; i++)
{
if (!vis[i])
{
ll t=,mn=inf,sum=; int j=i;
while (!vis[j])
{
vis[j]=; t++; sum+=v[a[j]]; mn=min(mn,v[a[j]]); j=cnt[a[j]];
}
if (t>=)
{
ll t1=sum+1ll*mn*(t-),t2=sum+mn+1ll*minn*(t+);
ans+=min(t1,t2);
}
}
}
printf("%lld\n",ans);
return ;
}