给定两个数字可重集相同的 (16) 进制数 (a,b) 之差(长度均相同,可以有前导 (0)),求最小的 (b),需判断无解。
( ext{len}le 14)。
设 (c=a-b),则退位次数是 (c) 的数位和 (/15),(inom{13}6) 枚举退位,就可以知道 (a_i-b_i) 的值。
假设 (b_i=a_{p_i}),则已知 (a_i-a_{p_i}),其中 (p) 是排列。
对于每个环,可以减到 (min=0),然后交换一下合并成一整个大环,对这个大环从 (0) 的位置开始 dp,记录下当前用了哪些 (a_i-a_{p_i}) 即可,如果得到的数位都 (in[0,15]) 说明合法。
然后还可以发现最高位一定是 (0):若 (c) 的最高位是 (15) 则 (b) 的最高位只能是 (0),否则合法解 (c/15) 的最高位是 (0)。
时间复杂度 (O(inom{13}{6}n2^n))。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1<<13;
const LL INF = 0x3f3f3f3f3f3f3f3f;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int num(char c){return isdigit(c) ? c-'0' : c-'a'+10;}
char chr(int c){return c < 10 ? c+'0' : c-10+'a';}
int n, m, lim, a[14];
LL dp[N], sum[N], ans = INF;
char s[15];
void dfs(int d, int c){
if(d == n-1){
if(a[n-1] < 0 || a[n-1] > 15) return;
memset(dp, 0x3f, lim<<3);
sum[0] = a[n-1]; dp[0] = 0;
for(int i = 1;i < lim;++ i)
sum[i] = sum[i&i-1] + a[__builtin_ctz(i)];
for(int i = 0;i < lim;++ i) if(sum[i] >= 0 && sum[i] < 16)
for(int j = 0;j < n-1;++ j) if(!(i>>j&1))
chmin(dp[i|(1<<j)], dp[i] + (sum[i]<<(j<<2)));
chmin(ans, dp[lim-1]);
return;
}
if(c < n-1-d) dfs(d+1, c);
if(c > 0){
a[d] -= 16; ++ a[d+1]; dfs(d+1, c-1);
a[d] += 16; -- a[d+1];
}
}
int main(){
scanf("%s", s); n = strlen(s); lim = 1<<n-1;
for(int i = 0;i < n;++ i) m += a[i] = num(s[n-1-i]);
if(m % 15){puts("NO"); return 0;}
dfs(0, m /= 15);
if(ans == INF) puts("NO");
else for(int i = n-1;~i;-- i) putchar(chr(ans>>(i<<2)&15));
}