E. Obtain a Permutation

题意:给定一个n*m的矩阵,你需要通过操作变成目标矩阵,每一次操作可以是修改一个数,可以是将一列循环上升一位,求最小的操作次数。

分析

其实这题只能算是一个暴力题,每一列其实是独立的,把每一列的最小操作次数求出来累加就是结果。而对于每一列,需要统计出每一个元素需要几次上升操作能够移动到正确位置上,用step[i] = c 表示,此列向上移动i次,可以有c个元素移动到正确位置上。当统计完一列之后复杂度是O(n),然后再进行n次循环进行ans = min(ans,i+n-step[i]) i:上升操作次数,n-step[i]改变次数,复杂度也是O(n),加在一起还是O(n)
然后累加ans就可以了

代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e6+10;

int N,M,res;
int arr[maxn],step[maxn];
int main(){
    cin>>N>>M;
    for(int i = 1;i<=N;i++){
        for(int j = 1;j<=M;j++){
            scanf("%d",&arr[i*M+j]);
        }
    }
    for(int j = 1;j<=M;j++){
        memset(step,0,sizeof(int)*N+10);//让初始化与N的大小有关
        int mi = 1e9;
        for(int i = 1;i<=N;i++){
            int cur = arr[i*M+j];
            if(cur>N*M || (cur-j)%M != 0) continue;
            int idx = (cur+M-1)/M;
            int mov = i>=idx? i-idx:i+N-idx;
            step[mov]++;
        }
        for(int k = 0;k<=N;k++){
            mi = min(mi,k + N -step[k]);
        }
        res += mi;
    }
    cout<<res<<endl;
    return 0;
}