cf1294E

题意简述:给一个矩阵,有两种操作可以进行

操作1:改变矩阵中一个元素的值

操作2:将矩阵中某一列的值循环下移

要求用最少的操作次数使得矩阵变成

题解:对于一列来说,我们肯定是先变化然后再循环下移,所以应该考虑变化哪些数字,而要知道变化哪些数字必须知道变化之后对应哪个循环,

比如3 1 4 ,可以变为 1 4 7 , 4 7 1 , 7 1 4 这三种,我们可以枚举他变成哪一种,然后计算需要的最少操作次数,这样明显会超时

反过来考虑,对于每一个元素来说,找他对哪几种最终的循环有贡献,也就说变成这种循环,他的值不需要改变,可以用dp[i]来表第i种循环,循环扫描每一个元素,更新dp数组就好

#include<bits/stdc++.h>
#define forn(i, n) for (int i = 0 ; i < int(n) ; i++)
#define fore(i, s, t) for (int i = s ; i < (int)t ; i++)
#define fi first
#define se second
#define all(x) x.begin(),x.end()
#define pf2(x,y) printf("%d %d
",x,y)
#define pf(x) printf("%d
",x)
#define each(x) for(auto it:x)  cout<<it<<endl;
#define pii pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=4e5+5;
const int maxm=2e5+5;
const int inf=1e9;

int n,m;

vector<vector<int>> a;

int dp[maxn];

int calc(vector<int> v,int col){
	for(int i=0;i<n;i++)
		dp[i]=0;
	for(int i=0;i<v.size();i++){
		int x=(v[i]-col)/m,y=v[i]%m==0?m:v[i]%m;
		//cout<<x<<' '<<y<<endl;
		if(y==col) {
			if(x>=i) dp[x-i]++;
			dp[x+n-i]++;
		}
	}
	int re=1e9;
	for(int i=0;i<n;i++)
		re=min(re,(n-dp[i])+(n-i)%n);
//	pf(re);
	return re;
}	
int main(){
	cin>>n>>m;
	a.resize(n,vector<int>(m,0));
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			scanf("%d",&a[i][j]);
	int ans=0;
	for(int i=0;i<m;i++){
		vector<int> v(n);
		for(int j=0;j<n;j++)
			v[j]=a[j][i];
		ans+=calc(v,i+1);
	}
	pf(ans);
}

  

原文地址:https://www.cnblogs.com/033000-/p/12378167.html