https://www.luogu.org/problemnew/show/P2127
感觉题解里写的比较复杂,可能自己的想法比较简单一点吧。
看这个图中的的点如果形成一个环,贪心的考虑,要想花费最少,那么我们一定要多次利用最小那个点。
图中来看就是2和6交换,然后2和4交换,然后就交换完了。
然而还有一种换法就是,这个环中最小的点和序列中最小的点先交换,然后在进行环内的操作(就是说先2和1交换...)。
所以对于每个点dfs找到环,然后判断两种方法哪种更优,统计答案。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define LL long long #define inf 2147483647 LL n,a[100006],b[100006],MIN,pos[100006],siz,sum,minn; bool vis[100006]; LL ans; void dfs(int x) { if(vis[x])return ; sum+=a[x]; //记录环内每个点的总和 siz++; //环内有多少个点 MIN=min(MIN,a[x]); //环内最小值 vis[x]=1; //打上标记 dfs(pos[x]); //下一个点 } int main() { scanf("%d",&n); minn=MIN=inf; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); b[i]=a[i]; minn=min(minn,a[i]); //记录序列最小值 } sort(b+1,b+1+n); //排序 for(int i=1;i<=n;i++) pos[i]=lower_bound(b+1,b+1+n,a[i])-b; //记录排序后的位置 for(int i=1;i<=n;i++) { if(!vis[i]) { dfs(i); ans+=min(sum+MIN+minn*(siz+1),sum+MIN*(siz-2)); /* 方法一:交换序列最小值和环内最小值 + 序列最小值 * 环内点数 方法二:环内最小值*环内点数。 */ siz=0; sum=0; MIN=inf; } } printf("%lld",ans); }