Codeforces EC #36 C. Permute Digits

看上去不难,但一直在WA…
开始的算法是:从左往右构造结果res[i],在a没被选择的数字中,选择<=b[i]的最大值,如果选择的值<
b[i],之后每一步都选择剩余数字中最大的。
问题在于:有可能选择了某个数字,导致之后不可能再构造出<=b的结果

123456789123456789
276193619183618162
Output
276193619�88755443
Answer
276193618987554432

两种办法:直接的是,如果构造到某个位置i,没有<=b[i]的值,则像退栈一样,直到退到某一位,可以选择比这一位当前选择的值更小的。选择这个更小的值,继续进行构造。
题解的做法:从左往右构造结果,选择的数字为:选择了这个数字后,之后一定可以构造出<=b的结果;每一步选择符合此条件的最大数字。判断之后能否有<=b的结果:把剩余数字从小到大放在后面,得到的数字<=b则能够构造出。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

char a[20], b[20];
bool used[20];
int sa, sb, ans[20];

// max char <= ch
int choose(char ch) {
  int mx = -1;
  for (int i = 0; i < sa; i++) {
    if (!used[i] && a[i] <= ch && (mx == -1 || a[i] > a[mx])) {
      mx = i;
    }
  }
  return mx;
}

// judge if b[i] > a[ans[j]]
bool lower(int j) {
  for (int i = 0; i < j; i++) {
    if (a[ans[i]] < b[i]) {
      return true;
    }
  }
  return false;
}

int main() {
  scanf("%s %s", a, b);
  memset(used, false, sizeof(used));
  sa = strlen(a);
  sb = strlen(b);
  // strlen(b) >= strlen(a)
  if (sb > sa) {
    int j = 0;
    for (int i = 0; i < sa; i++) {
      int mx = choose('9');
      ans[j++] = mx;
      used[mx] = true;
    }
  } else {
    int j = 0;
    for (int i = 0; i < sb; i++) {
      int mx = choose(lower(j) ? '9' : b[i]);
      if (mx == -1) {
        j--;
        // cannot choose smaller
        while (choose(a[ans[j]] - 1) == -1) {
          used[ans[j--]] = false;
        }
        used[ans[j]] = false;
        ans[j] = choose(a[ans[j]] - 1);
        used[ans[j]] = true;
        i = j++;
      } else {
        ans[j++] = mx;
        used[mx] = true;
      }
    }
  }
  for (int i = 0; i < sa; i++) {
    printf("%c", a[ans[i]]);
  }
  printf("\n");

  return 0;
}
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

char a[20], b[20], res[20];
int counts[15];

bool smaller(char* res) {
  if (strlen(b) > strlen(res)) return true;
  for (int i = 0; i < strlen(res); i++) {
    if (res[i] > b[i]) return false;
    else if (res[i] < b[i]) return true;
  }
  return true;
}

bool check(int i, int j) {
  res[i++] = j + '0';
  for (int k = 0; k <= 9; k++) {
    int t = counts[k];
    if (k == j) t--;
    while (t--) {
      res[i++] = k + '0';
    }
  }
  res[i] = '\0';
  return smaller(res);
}

int main() {
  scanf("%s %s", a, b);
  memset(counts, 0, sizeof(counts));
  for (int i = 0; i < strlen(a); i++) {
    counts[a[i] - '0']++;
  }
  for (int i = 0; i < strlen(a); i++) {
    int j;
    for (j = 9; j >= 0; j--) {
      if (counts[j] == 0) continue;
      if (check(i, j)) break;
    }
    res[i] = j + '0';
    counts[j]--;
  }
  for (int i = 0; i < strlen(a); i++) {
    printf("%c", res[i]);
  }
  printf("\n");
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值