E. Obtain a Permutation

给定一个n×m矩阵,目标是将其还原到特定排列。有两种操作:单点数值修改和整列上移。问题求解最小操作次数。通过分析发现操作互相独立,对于每列,最优操作次数是min(0≤i<n)(n-samej,i+i),其中samej,i表示第j列上移i单位后不变的数。通过判断条件找出每列的same值,计算总操作数。提供了解题思路和代码实现。" 110309058,9159331,数据挖掘比赛:log表处理与模型泛化性探讨,"['数据清洗', '数据处理', '模型泛化', '特征工程', '数据挖掘比赛']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
给一个 n×m 的矩阵,你想要把它还原成如下的矩阵
在这里插入图片描述
你有两种操作:
• 选择一个数,修改它的值。
• 选择一列,每个元素上移一位,第一行的元素移到第 n 行。
求还原的最小操作次数。

数据范围:
1≤n,m≤2⋅10^5
1≤n⋅m≤2⋅10^5

思路:
对于任意一行,我们可以发现,无论是先进行单点修改,后进行整列上移,还是先进行整列上移,后进行单点修改。都是没有影响的,两种操作是互相独立的。所以我们可以设定samej,i表示第j列上移i个单位后,有多少个数是不需要改变的,这时我们可以得到对于任意一列,操作次数是n-samej,i+i,因为n-samej,i是需要改变的操作次数,i是上移的次数,所以对于任意一列,最优答案就为min(0≤i<n)(n-samej,i+i)。所以最终整体答案为所有列最优答案的求和。这时我们就只需要求出same即可,在判断ai,j是否属于第j列的数字时,有三个条件:j≤ai,j , ai,j≤m*n , (ai,j-j) % m = 0.满足这三个条件,就可以说明ai,j是第j列的数字。然后我们还需要判断ai,j应该在第几行,我们设ai,j应该在第k行。通过还原后的矩阵得到公式k=(ai,j-j)/m+1。最后判断当前列和第k列的位置,如果k≤i,就same[i-k]++,如果k>i,就same[i-k+n]++.最后通过最上面的公式求得答案即可。

代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int mod=1e9+7;
int m,n,same[maxn],ans;
vector<int> a[maxn];  //开vector容器存矩阵
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		a[i].push_back(0); //保证下标为1
		for(int j=1;j<=m;j++)
		{
			int t;cin>>t;
			a[i].push_back(t);
		}
	}
	for(int j=1;j<=m;j++)
	{
		int cnt=INT_MAX;
		for(int i=0;i<n;i++)
			same[i]=0;
		for(int i=1;i<=n;i++)
		{
			if(a[i][j]>=j&&a[i][j]<=(m*n)&&(a[i][j]-j)%m==0)
			{
				int k=(a[i][j]-j)/m+1;
				if(i>=k)
					same[i-k]++;
				else
					same[i-k+n]++;
			}
		}
		for(int i=0;i<n;i++)
			cnt=min(cnt,i+n-same[i]);
		ans+=cnt;
	}
	cout<<ans;
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值