D - Swapping Puzzle(AtCoder Beginner Contest 332)

题目:D - Swapping Puzzle (atcoder.jp)

样例输入:

4 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
1 3 2 5 4
11 13 12 15 14
6 8 7 10 9
16 18 17 20 19

样例输出:

3

题目翻译成人话:

给你n行m列的数组a和b, 让你对a数组任意交换任意两行或者任意两列,问你最少需要多少步,能让数组a等于数组b,不存在答案的情况下输出-1

正确的分析:

交换之后,我们关注的是结果,而不是你交换的哪一行,或者是哪一列? 这个时候就用到了全排列,对行用一次,对列用一次全排列。然后去check一下数组a是否等于数组b,要是相等的话,怎么去确定他们交换的次数呢? 逆序数!为什么是逆序数呢?
这个问题涉及到数学中的排列理论。首先,让我们来解释一下为什么是逆序数。


在一个由1到n组成的排列中,如果一个数比它后面的某个数大,那么它们就构成了一个逆序对。逆序数就是这样的逆序对的个数。
现在考虑将一个由1到n组成的排列打乱之后,我们需要通过交换操作将其变回有序状态。每次交换两个数时,它们之间的相对顺序会发生改变,因此交换次数与逆序数之间存在一种对应关系。
假设初始排列为a1, a2, ..., an,目标排列为1, 2, ..., n。我们从初始排列开始,每次选择一个位置进行交换,直到排列变为有序的目标排列。在进行交换的过程中,逆序对的数量会逐渐减小,最终变为0。
因此,初始排列到目标排列的交换次数就等于初始排列的逆序数,这就解释了为什么数组打乱后它们交换的次数是它们的逆序数。


这里的全排列直接可以用c++里面的 next_permutation()

正确的代码:

#include<bits/stdc++.h>
#define y1 Y1
#define fi first
#define endl "\n"
#define se second
#define PI acos(-1)
#define int long long
#define pb(x) push_back(x)
#define PII pair<int, int>
#define Yes cout << "Yes\n";
#define No cout << "No\n";
#define YES cout << "YES\n";
#define NO cout << "NO\n";
#define _for(i, a, b) for(int i = a; i <= b; ++i)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int N = 10;

int a[N][N], b[N][N];
int n, m, ret = 0x3f3f3f3f;
bool h[N][N], l[N][N];
int hp[N], lp[N];



bool check() {
	_for(i, 1, n) {
		_for(j, 1, m) {
			if(a[hp[i]][lp[j]] != b[i][j]) {
				return false;
			}
		}
	}
	return true;
}

int reverseNumber() {//逆序数 
    int t = 0;
    for(int i = 1; i <= n; ++ i ) {
    	for(int j = 1; j <= i; ++ j) {
    		if(hp[i] < hp[j])t++;
		}
	}
	
	for(int i = 1; i <= m; ++ i ) {
		for(int j = 1; j <= i; ++ j ) {
			if(lp[i] < lp[j])t++;
		}
	}
	return t;	
}

void dfsL() {
	do{
		if(check()) {
			ret = min(ret, nxs());
		}
	}while(next_permutation(lp + 1, lp + m + 1));
}

void dfsH() {
	do{
		dfsL();
	}while(next_permutation(hp + 1, hp + n + 1));
}

signed main() {
	IOS;
	cin >> n >> m;
	
	_for(i, 1, n) {
		_for(j, 1, m) {
			cin >> a[i][j];
		}
	}
	
	_for(i, 1, n) {
		_for(j, 1, m) {
			cin >> b[i][j]; 
		}
	}
	_for(i, 1, max(n, m)) {
		hp[i] = i;
		lp[i] = i;
	}
    dfsH();
	if(ret == 0x3f3f3f3f)ret = -1;
	cout << ret << endl;
	return 0;
}

正确的运行结果:

自己去实现全排列:

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int a[N], n;
bool st[N];

void dfs(int len) {
	if(len == n + 1) {
		for(int i = 1; i <= n; ++ i ) {
			cout << a[i] << ' ';
		} 
		cout << endl;
	}
	for(int i = 1; i <= n; ++ i ) {
		if(!st[i]) {
			a[len] = i;
			st[i] = true;
			dfs(len + 1);
			st[i] = false;
		} 
	}
}

signed main() {
	cin >> n;
	for(int i = 1; i <= n; ++ i) {
		a[i] = i;
	}
	dfs(1);
	return 0;
}

运行结果:

我之前的思路(错误的,但不知道错在哪了):

思路:

无脑交换呗,直接暴力枚举,交换,

代码:

#include<bits/stdc++.h>
#define y1 Y1
#define fi first
#define endl "\n"
#define se second
#define PI acos(-1)
#define int long long
#define pb(x) push_back(x)
#define PII pair<int, int>
#define Yes cout << "Yes\n";
#define No cout << "No\n";
#define YES cout << "YES\n";
#define NO cout << "NO\n";
#define _for(i, a, b) for(int i = a; i <= b; ++i)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int N = 10;

int a[N][N], b[N][N];
int n, m, ret = 0x3f3f3f3f;
bool h[N][N], l[N][N];

void swapH(int x, int y) {//交换的是行数  对应的列发生改变 
	for(int j = 1; j <= m; ++ j ) {
		swap(a[x][j], a[y][j]);
	}
}

void swapL(int x, int y) {//交换的是列数 对应的行发生改变 
	for(int i = 1; i <= n; ++ i ) {
		swap(a[i][x], a[i][y]);
	}
}

bool check() {
	_for(i, 1, n) {
		_for(j, 1, m) {
			if(a[i][j] != b[i][j]) {
				return false;
			}
		}
	}
	return true;
}
//暴搜是不行的 怎么去剪枝呢 
void dfsL(int len, int sum) {//枚举的是交换的列数 
	if(sum >= ret)return;
	if(len == m) {
		if(check()) {
			ret = min(ret, sum);
		}	
		return;
	}
	for(int i = 1 + len; i <= m; ++ i ) {
		dfsL(len + 1, sum);//不换 
		swapL(len, i);//交换列 
		dfsL(len + 1, sum + 1);//换  
		swapL(len, i);//回溯 
	}	
}

void dfsH(int len, int sum) {// 枚举的是交换的行数 
    if(sum >= ret)return;
    if(len == n) {
    	dfsL(1, sum);
    	return ;
	}	
	for(int i = 1 + len; i <= n; ++ i ) {
	    dfsH(len + 1, sum); // 不换 
    	swapH(len, i); //交换行 
	    dfsH(len + 1, sum + 1); // 换
	    swapH(len, i);// 回溯 
	}
}

signed main() {
	IOS;
	cin >> n >> m;
	_for(i, 1, n) {
		_for(j, 1, m) {
			cin >> a[i][j];
		}
	}
	
	_for(i, 1, n) {
		_for(j, 1, m) {
			cin >> b[i][j]; 
		}
	}
	dfsH(1, 0);
	if(ret == 0x3f3f3f3f)ret = -1;
	cout << ret << endl;
	return 0;
}

运行结果(没过):
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FindYou.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值