题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1119
https://www.lydsy.com/JudgeOnline/problem.php?id=1697
先找到置换的循环节。发现对于同一个循环节里的元素,可以找一个代价最小的元素,用它把所有元素换到位置上。
每次交换一定有一个元素到自己的位置上了(不然不优);最后一次是两个元素都弄好了;所以一共是 ( n-1 ) 次。其中,每个元素贡献一次,剩下的 2*(n-1) - n 次贡献就可以选择代价最小的那个元素了。
还以为这样就是最优的。
然而其实还可以在循环节外面找一个最小的元素来和自己换。这样的话除了第一次把这个外面的元素换进循环里,剩下每一次交换都有一个元素到了它应该在的位置,所以一共是 ( n+1 ) 次。其中,每个元素贡献一次,被换出去的元素贡献两次,换进来(最后又换出去)的元素贡献 2*(n+1)-n-1 次。和上面情况取 min 即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e6+5,M=6505; int n,w[N],a[N],b[N],tot; ll ans; bool vis[N]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } ll Mn(ll a,ll b){return a<b?a:b;} int main() { n=rdn(); int tmn=M; for(int i=1;i<=n;i++)w[i]=rdn(),tmn=Mn(tmn,w[i]); for(int i=1;i<=n;i++)a[i]=rdn(); for(int i=1;i<=n;i++)b[rdn()]=i; for(int i=1;i<=n;i++) if(!vis[i]) { int cr=i,mn=M;ll sm=0; tot=0; while(!vis[cr]) { tot++; vis[cr]=1; mn=Mn(mn,w[a[cr]]); sm+=w[a[cr]]; cr=b[a[cr]]; } ans+=Mn((ll)mn*(tot-1)+sm-mn,(ll)tmn*(tot+1)+sm+mn); } printf("%lld ",ans); return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e4+5,M=1e5+5; int n,a[N],b[N],c[M],ans; bool vis[N]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} int main() { n=rdn(); int tmn=M; for(int i=1;i<=n;i++)a[i]=rdn(),tmn=Mn(tmn,a[i]); for(int i=1;i<=n;i++)b[i]=a[i]; sort(b+1,b+n+1); for(int i=1;i<=n;i++)c[b[i]]=i; for(int i=1;i<=n;i++) if(!vis[i]) { int cr=i,tot=0,mn=M,sm=0; while(!vis[cr]) { tot++; vis[cr]=1; mn=min(mn,a[cr]); sm+=a[cr]; cr=c[a[cr]]; } ans+=Mn( mn*(tot-1)+sm-mn,tmn*(tot+1)+sm+mn ); } printf("%d ",ans); return 0; }