几道置换群题目

最近学了置换群,然后就想着写几道

POJ - 1026 Cipher

VJ

题意:

题目意思是给一个置换,然后给一些字符串和一个整数k,要求对这些字符串进行k次置换

思路:

非常基础的题,要求置换k次,那么就把置换中的每一个环向右转 k − 1 k - 1 k1次,然后再置换即可。

代码:

这里我写复杂了

#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#include<cstring>
#define INF 0x3f3f3f3f
#define ll long long
#define pb push_back
#define eps 1e-8
#define endl '\n'
using namespace std;
const ll maxn = 1e4 + 5;
int n,p[maxn],k,np[maxn],cnt,X[205],Y[205];
char str[maxn],ans[maxn];
bool vis[maxn];
vector<int> R[maxn];
int main(){
    
    
    while(~scanf("%d",&n) && n){
    
    
        for(int i = 1; i <= n; i++)
            scanf("%d",p + i);
        cnt = 0;
        for(int i = 1; i <= n; i++)vis[i] = 0;
        for(int i = 1; i <= n; i++){
    
    
            if(!vis[i]){
    
    
                int x = p[i],cnt2 = 0;
                R[++cnt].clear();
                while(!vis[x]){
    
    //找环,用vector存环,并用X,Y存每个数在第几个环,第几个位置
                    vis[x] = 1;
                    X[x] = cnt;
                    Y[x] = cnt2;
                    R[cnt].push_back(x);
                    x = p[x];
                    cnt2++;
                }
            }
        }
        while(scanf("%d",&k) && k){
    
    
            getchar();
            gets(str + 1);
            int len = strlen(str + 1);
            for(int i = 1; i <= n; i++){
    
    
                int r = R[X[p[i]]].size(),idx = X[p[i]];
                char &c = ans[R[idx][(Y[p[i]] + k - 1) % r]];//转k-1次后的位置
                if(i <= len)
                    c = str[i];
                else
                    c = ' ';
            }
            ans[n + 1] = '\0';
            printf("%s\n",ans + 1);
        }
        printf("\n");
    }
    return 0;
}

POJ 3270 Cow Sorting

VJ

题意:

给了一些n不相同的数字,让你把这些数字进行从小到大排序,你可以每次交换两个数字,需要花费两个数字之和的代价,现在让求最小代价。

思路:

看到每个数字不相同就可以想到置换群了,首先先把所有的数字小到大按照1-n上号。然后就可以往置换的环上去想了。
比如: 4 2 1 3
按顺序写出他的环就是 4 3 1 2,这个顺序表示左侧数的位置在排序后应该是右侧的数。
那么就可以想到可以找到一个数,与所有数进行倒着交换比如:
swap(2,1),swap(2,3),swap(2,4)
可以发现在交换环的大小减一次时就可以完成排序

那么贪心的想法就是,找一个环最小的数与所有数进行交换(环内最小设为minn1,环所有数的和设为sum,环的大小设为r),花费为 s u m + ( r − 1 ) ∗ m i n n 1 sum +(r - 1)* minn1 sum+(r1)minn1

但是这还不够,因为环外还有可能会有更小的数,所以如果引入一个环外的数有可能会造成更小的话费,引入环外的数的话就需要额外进行进行交换,即拿本环内的最小的先与环外的交换,然后再换回来,环内最小设为minn1,环外的设为minn2,环的大小设为r,环所有数的和设为sum,那么这种交换的花费就是 s u m + ( r + 1 ) ∗ m i n n 2 + m i n n 1 sum + (r+ 1) * minn2 + minn1 sum+(r+1)minn2+minn1

#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#define INF 0x3f3f3f3f
#define ll long long
#define pb push_back
#define eps 1e-8
#define endl '\n'
using namespace std;
const ll maxn = 1e6 + 5;
int n,p[maxn],mp[maxn];
ll ans,minn = INF;
bool vis[maxn];
vector<ll> v;
struct node{
    
    
    int val,idx;
}PP[maxn];
bool cmp(node a,node b){
    
    
    return a.val < b.val;
}
void calculateValue(){
    
    
    if(v.size() == 1)return;
    ll sum = 0,len = v.size(),mi = INF;
    for(int i = 0; i < len; i++)
        sum += mp[v[i]],mi = min(mi,(ll)mp[v[i]]);
    if(mi == minn)
        ans += sum + (len - 2) * mi;
    else
        ans += min(sum + (len - 2) * mi, sum + (len + 1) * minn + mi);
}
int main(){
    
    
    while(~scanf("%d",&n)){
    
    
        for(int i = 1; i <= n; i++)vis[i] = 0;
        ans = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d",p + i),PP[i].val = p[i],PP[i].idx = i,minn = min(minn,(ll)p[i]);
        sort(PP + 1,PP + n + 1,cmp);
        for(int i = 1; i <= n; i++){
    
    
            mp[i] = PP[i].val;
            p[PP[i].idx] = i;
        }
        for(int i = 1; i <= n; i++){
    
    
            if(!vis[i]){
    
    
                int x = p[i];
                v.clear();
                while(!vis[x]){
    
    
                    v.push_back(x);
                    vis[x] = 1;
                    x = p[x];
                }
                calculateValue();
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36102055/article/details/107500421
今日推荐