很睿智的题解
补题的时候看题解,next_permutation枚举我理解,但是看到solve()内部判断当前下标是不是等于i的时候实在懵逼,查了好几篇终于明白了。题解的暴力思路是这样的,限定了交换次数,之后问我们在这个次数下的最大值,最小值。通过next_permutation找出所有可能的排列情况,跟原本的序列号对比(就是i到length的自然序),如果出现不相同,说明出现了一次swap,再向后找当前自然序位置在permutation数组里的位置,两者交换,算一次swapcnt。这样扫下去,如果扫的过程中,swapcnt>k,说明这种排列违法,直接return,进行下一种排列。因为这种情况寻求的是全部合法情况,所以max,min一定包含其中,在函数末尾搞一个min(),max(),全局维护max,min即可,不需要分最大最小分别讨论。也正因此只要判断第一位的情况就可以了,不管是min max第一位都不可以为0,直接return向后进行。
不过这样之后还是T,试了一下直接贴题解的码上去也T了orz实在是非常迷了。
最后发现同样是下面的码,如果是1-len操作就会T,0-len-1操作就可以AC,我也是很???了orz
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int mino[10],
numcnt[10], numcnt2[10],//0-9
permu[10], permucopy[10];
int main()
{
int T = 0;
scanf("%d", &T);//读入测试样例的数目
while(T--)
{
//memset(o, '\0', sizeof(o));
//memset(mino, 0, sizeof(mino));
memset(numcnt, 0, sizeof(numcnt));
memset(numcnt2, 0, sizeof(numcnt2));
//memset(permucopy, 0, sizeof(permucopy));
int n, k;
scanf("%d %d", &n, &k);
int len = 0;
while(n)
{
permu[len++] = n%10;
//printf("permu[%d] = %d\n", len, permu[len]);
numcnt[n%10]++;
numcnt2[n%10]++;
n /= 10;
}
//printf("len = %d\n", len);
for(int i = len - 1;i >= 0;i--)
{
mino[len - i -1] = permu[i];
//printf("permu[%d] = %d mino[%d] = %d\n", i+1, permu[i+1], len-i, mino[len-i]);
}
for(int i = 0;i < len;i++)
permu[i] = i;
int minresult = 0x3f3f3f3f, maxresult = -1;
if(k >= len-1)
{
//输出最小值
for(int i = 1;i <= 9;i++)
{
if(numcnt[i])
{
printf("%d", i);
numcnt[i]--;
break;
}
}
for(int i = 0;i <= 9;i++)
{
while(numcnt[i])
{
printf("%d", i);
numcnt[i]--;
}
}
printf(" ");
//输出最大值
for(int i = 9;i >= 0;i--)
{
while(numcnt2[i])
{
printf("%d", i);
numcnt2[i]--;
}
}
printf("\n");
continue;
}
do
{
int sum = 0, swapcnt = 0;
if(mino[permu[0]] == 0)
continue;
for(int i = 0;i < len;i++)
permucopy[i] = permu[i];
for(int i = 0;i < len;i++)
{
if(permucopy[i] != i)//发生过交换
{
swapcnt++;
if(swapcnt > k)
break;
for(int j = i+1;j < len;j++)
{
if(permucopy[j] == i)
{
swap(permucopy[i], permucopy[j]);
break;
}
}
}
if(swapcnt > k)
break;
sum = sum*10 + mino[permu[i]];
}
if(swapcnt <= k)
{
maxresult = max(maxresult, sum);
minresult = min(minresult, sum);
}
}while(next_permutation(permu, permu+len));
printf("%d %d\n", minresult, maxresult);
}
return 0;
}
TLE的本人
给你一串数字,和k上限的交换次数,问在k内最大可生成的值和最小可生成的值分别是多少。我的想法是,不论求最大最小,都是第x大/小的数在第x位置上,如果原本的数符合条件,那么跳过,不占用交换次数,循环到下一轮。所以我用struct来储存pos val的信息,写一个cmp来满足val升序排列,pos降序排列。之后依次取用。
对于0的特殊情况,设置一个used属性,如果使用过就标记为1,对于有0的情况,临时变量跳到第一个不是0的值,然后标记为1,但是全局操纵digit下标的值保持不变,每次进行swap的时候先检验一下used的值,如果为1就往后找。
但是可能是因为这样的花销太大,加上为了确认交换之后两边的pos值都要改变,需要一个getit函数取找在digit里的位置,所以一直T。目前就是这样,但是找不到更好的优化方式了。
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int T, n, k, size_;
char integer[10], integermax[10];
typedef struct nod
{
int pos, //位置
val; //值
bool used = 0,
zeroflag = 0;
}sw;
sw digit[10];
sw digit2[10];
bool cmp(sw x, sw y)
{
if(x.val == y.val)
return x.pos > y.pos;//降序排列
return x.val < y.val;//升序排列
}
int findit(int val, int pos)
{
//printf("findit into val = %d pos = %d\n", val, pos);
int l = 0, r = size_-1, mid = 0;
//printf("l = %d r = %d mid = %d\n", l, r, mid);
/*printf("\n startgetit\n");
for(int i = 0;i < size_;i++)
{
printf("digit[%d].val = %d\n", i, digit[i].val);
}*/
while(l <= r)
{
//printf("into while\n");
mid = l+(r-l)/2;
//printf("l = %d r = %d mid = %d\n", l, r, mid);
//printf("digit[%d].val = %d .pos = %d\n", mid, digit[mid].val, digit[mid].pos);
if(digit[mid].val < val)
{
l = mid+1;
}
else if(digit[mid].val > val)
{
r = mid-1;
}
else
{
//printf("l = %d r = %d\n", l, r);
for(int i = l;i <= r;i++)
if(digit[i].pos == pos)
{
//printf("mid = %d\n", i);
return i;
}
}
}
}
int main()
{
//printf("start\n");
scanf("%d", &T);
while(T--)
{
//printf("\n\n");
memset(digit, 0, sizeof(digit));
memset(digit2, 0, sizeof(digit2));
memset(integer, '\0', sizeof(integer));
memset(integermax, '\0', sizeof(integermax));
scanf("%s %d", integer, &k);
for(int i = 0;i < strlen(integer);i++)
{
digit[i].pos = i;
digit[i].val = integer[i] - '0';
digit2[i].pos = i;
digit2[i].val = integer[i] - '0';
//copydigit[i].pos = i;
//copydigit[i].val = integer[i] - '0';
integermax[i] = integer[i];
}
//排序 从小排到大 更换k个
sort(digit, digit + strlen(integer), cmp);
/*for(int i = 0;i < strlen(integer);i++)
{
printf("%d %d\n", digit[i].val, digit[i].pos);
}*/
size_ = strlen(integer);
//printf("size = %d\n\n", size_);
if(k >= size_-1)
{
int tr = 0;
while(digit[tr].val==0)
tr++;
digit[tr].used = 1;
printf("%d", digit[tr].val);
for(int i = 0;i < size_;i++)
{
if(digit[i].used==0)
printf("%d",digit[i].val);
}
printf(" ");
for(int i = size_-1;i >= 0;i--)
printf("%d",digit[i].val);
printf("\n");
continue;
}
//找最大
int i = 0, cnt = 0;
while(cnt < k && i < size_)//k统计当前swap的次数,i表示当前的位置
{
//printf("integermax - %s\n", integermax);
if(digit[size_-1-i].val > integermax[i] - '0')//如果当前位置小于digit对应位置元素
{
//printf("integermax[%d] = %c\n", i, integermax[i]);
//printf("digit[%d] integermax[%d] = %c\n", size_-1-i, digit[size_-1-i].pos, integermax[digit[size_-1-i].pos]);
integermax[digit[size_-1-i].pos] = integermax[i];//当前元素值赋给digit位置处的元素
/**如何维护被替换元素的pos值 注意要在integermax[i]改变前**/
//printf("\nfindit = %d\n", findit((integermax[i]-'0'), i));
digit[findit((integermax[i]-'0'), i)].pos = digit[size_-1-i].pos;
//printf("digit[%d].pos = %d\n", findit((integermax[i] - '0'), i), digit[findit(integermax[i], i)].pos);
integermax[i] = digit[size_-1-i].val + '0';//digit的值赋给当前的元素
//printf("integermax - %s\n", integermax);
digit[size_-1-i].pos = i;//当前替换元素的pos改变
i++;
cnt++;
continue;
}
i++;
}
//找最小
int flag = 0, //记录跳0操作
digiti = 0;//记录digiti内部进行到了第几位
i = 0, //记录n移动到了第几位
cnt = 0;//记录操作了几次
while(cnt < k && i < size_ && digiti < size_)
{
/*printf("\n\ni = %d cnt = %d digiti = %d\n", i , cnt, digiti);
printf("integer - %s\n", integer);
printf("startwhile\n");
for(int i = 0;i < size_;i++)
{
printf("digit[%d].used = %d .val = %d .pos = %d\n", i, digit[i].used, digit[i].val, digit[i].pos);
}
*/
if(i == 0)//如果是数字n的第一位
{
if(digit2[i].val == 0)//数字n的第一位为0 需要进行跳零操作
{
//printf("i == 0 digit[i].val == 0\n");
int tim = 0;//digit从小到大排,如果有0一定在前面
while(digit2[tim].val == 0)
{
tim++;
}
digit2[tim].zeroflag = 1;//记录第一个不为0的digit的位置,并标记已经使用
digit2[tim].used = 1;
integer[digit2[tim].pos] = integer[i];//将i位置的值赋给tim位置的值
digit2[findit((integer[i]-'0'), i)].pos = digit2[tim].pos;//被替换元素pos值更新
integer[i] = digit2[tim].val + '0';//digittim位置的值赋给当前的元素
digit2[tim].pos = i;//当前替换元素的pos改变
flag = 1;//记录出现了跳0操作
cnt++;//进行了一次swap 交换次数++
i++;//数字n向后移位
continue;
}
else if(digit2[i].val < integer[i] - '0')//第一位不是0,但是digit内部有小于in[i]的值
{
//printf("i == 0 digit[i].val < integer[i] - '0'\n");
integer[digit2[i].pos] = integer[i];//将此时第1位的值赋给digit第一个小于i的位置值
digit2[findit((integer[i]-'0'), i)].pos = digit2[i].pos;//将digiti的pos更新给原本i位置元素的pos
integer[i] = digit2[i].val + '0';//digit的值赋给当前i位置的元素
digit2[i].pos = i;//当前替换元素的pos改变
digit2[i].used = 1;
cnt++;
i++;
digiti++;
continue;
}
//如果走到这里 说明虽然是第一位 但是digit中没有比这里更小的
//需要i++向后挪位,在while最后写了i++这里就不必i++了
}
else if(digiti == 0)//i不是n的第一位 digitii却为0 说明一定跳0 并且digitii对应的是0
{
//printf("digiti == 0\n");
if(integer[i] == '0')//是0
{
//printf("integer[i] == '0'\n");
//printf("%d\n", findit(integer[i]-'0', i));
digit2[findit(integer[i]-'0', i)].used = 1;//将这个0留在原本位置
//printf("endfun\n");
i++;
continue;
}
else//不是0
{
//printf("integer[i] != '0'\n");
while(digit2[digiti].used == 1)
digiti++;
integer[digit2[digiti].pos] = integer[i];
digit2[findit(integer[i]-'0', i)].pos = digit2[digiti].pos;
digit2[digiti].pos = i;
integer[i] = digit2[digiti].val + '0';
digit2[digiti].used = 1;
digiti++;
cnt++;
i++;
if(digit2[digiti].zeroflag == 1)
{
flag = 0;
}
continue;
}
}
/*else if(flag)//i!=1 但是出现过跳0 此时已经是n的第三位
{
if(digit[digiti].val < integer[i] - '0')
{
printf("i != 0 flag = 1 digit[timpos].val < integer[i] - '0'\n");
int tm = 0;
while(digit[digiti].used == 1)
{
if(digit[digiti].zeroflag == 1)
{
tm = 1;
break;
}
digiti++;
}
if(!tm)//当前元素未被使用
{
printf("!tm\n");
printf("inte = %s\n", integer);
integer[digit[digiti].pos] = integer[i];
printf("inte = %s\n", integer);
digit[findit((integer[i]-'0'), i)].pos = digit[digiti].pos;//被替换元素pos值更新
printf("inte = %s\n", integer);
integer[i] = digit[digiti].val + '0';//digit的值赋给当前的元素
printf("inte = %s\n", integer);
digit[digiti].pos = i;//当前替换元素的pos改变
digit[digiti].used = 1;
if(digit[digiti+1].zeroflag == 0)
digiti++;
else
{
digiti+=2;
flag = 0;
}
cnt++;
i++;
continue;
}
flag = 0;
digiti++;
}
}*/
else if(digit2[digiti].val < integer[i] - '0')//i!=1 跳零带来的问题也已解决
{
//printf("i != 0 flag = 0 digit[cnt].val < integer[i] - '0'\n");
//printf("digit.val = %d integeri = %d \n",digit[digiti].val , integer[i] - '0');
while(digit2[digiti].used == 1)
digiti++;
//printf("integer[digit[cnt].pos] = integer[%d] = %c integer[i = %d] = %c\n", digit[cnt].pos, integer[digit[cnt].pos], i, integer[i]);
integer[digit2[digiti].pos] = integer[i];
digit2[findit((integer[i]-'0'), i)].pos = digit2[i].pos;//被替换元素pos值更新
integer[i] = digit2[digiti].val + '0';//digit的值赋给当前的元素
digit2[digiti].pos = i;//当前替换元素的pos改变
digit2[digiti].used = 1;
cnt++;
i++;
digiti++;
continue;
}
//printf("一个分支都没走\n");
digit2[findit(integer[i]-'0', i)].used = 1;
i++;
}
printf("%s %s\n", integer, integermax);
}
return 0;
}