题目意思
给你T组数据,每组数据给你两个正整数n,k,你每次可以交换任意两个数,总的交换次数不超过k次,问你在k次之内这个数可以变成的最大值和最小值是多少
思路分析
下面所有思路分析都是看了杜老师代码才想到的。
首先对其预处理,枚举所有可能情况(init
函数):
枚举1-9的全排列,假设当前枚举到6,判断当前的排列是由123456经过多少次数字交换得到的,然后我们计算出这个排列的数值并压入v[6][交换次数]
。
代码中v[i][j]
指的是里面存的是i
位数字,并且可以通过j
次交换,将里面的数字转化为123…i
。
apply函数用来计算若数字下标按*it所指示的下标位置构成的新数字。此处apply函数传入一个数字(用来指示下标的数字),返回原数字按照 传入数字所指示下标顺序重排所构成的数字。
我们来看一组案例213 1
,以它的下标作为索引,计算其下标交换0次,1次所能构成的新数字,所以我们要遍历v[3][0],v[3][1],(定义原数字213,第一位下标为1,第二位下标为2,第三位为三)然后根据他们里面存的数字来将213重排,每一次重排,为了防止出现前导0,需要加一个判断,然后比较每一次所出现的值的大小,输出最大最小值。
由于这里涉及到重排比较麻烦,所以考虑使用数组存整数n,然后每次需要用到值的实话,再求出来。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
vector<int>v[10][10];
char s[100];
int k, flag;
int book[15], a[15];
void init()
{
for (int n = 1; n <= 9; n++)
{
for (int i = 1; i <= n; i++)
a[i] = i;
memset(book, 0, sizeof(book));
do
{
int now = 0;
for (int i = 1; i <= n; i++)
{
now *= 10;
now += a[i];
}
flag++;
int temp = 0;
for (int i = 1; i <= n; i++)
{
if (book[i] != flag)
{
book[i] = flag;
for (int j = a[i]; i != j; j = a[j])
{
book[j] = flag;
temp++;
}
}
}
v[n][temp].push_back(now);
} while (next_permutation(a + 1, a + n + 1));
}
}
int apply(int k)
{
int mul = 1;
int ans = 0;
while (k)
{
ans += a[k % 10] * mul;
k /= 10;
mul *= 10;
}
return ans;
}
int main()
{
init();
int t;
scanf("%d", &t);
while (t--)
{
scanf("%s %d", s, &k);
if (strlen(s) == 10)
{
printf("1000000000 1000000000\n");
continue;
}
int mx = 0;
int mn = 10000000000;
if (k >= strlen(s))
k = strlen(s) - 1;
for (int i = 1; i <= strlen(s); i++)
a[i] = s[i - 1] - '0';
int ff = 1;
for (int i = 1; i < strlen(s); i++)
ff *= 10;
for (int i = 0; i <= k; i++)
{
for (auto it = v[strlen(s)][i].begin(); it != v[strlen(s)][i].end(); it++)
{
int temp = apply(*it);
if (temp < ff)
continue;
mx = max(mx, temp);
mn = min(mn, temp);
}
}
printf("%d %d\n", mn, mx);
}
}