【2017年 腾讯校招笔试】【数位DP】 + 【解方程 贪心 正难则反】

来源:互联网 发布:兵棋推演软件 编辑:程序博客网 时间:2024/06/14 02:42

【2017年 腾讯校招笔试 A】【数位DP】2的幂次方的数字各2个,问你构成n的不同方案数

#include<stdio.h>#include<iostream>#include<string.h>#include<string>#include<ctype.h>#include<math.h>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }const int N = 1e6 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }int casenum, casei;LL n;int f[N];void table(){f[0] = 1;for (int i = 0; i < 20; ++i){int x = 1 << i;for (int j = 1e5; j >= 0; --j)if(f[j]){f[j + x] += f[j];f[j + x + x] += f[j];}}//for (int i = 0; i < 100; ++i){//printf("%d %d\n", i, f[i]);}}int TOP;LL ANS;void dfs(int p, bool use){if (p > TOP){if (!use)++ANS;return;}if (n >> p & 1){dfs(p + 1, 0);if (use)dfs(p + 1, 1);}else{if (!use)dfs(p + 1, 0);dfs(p + 1, 1);}}LL DFS(){ANS = 0;TOP = -1;for (int i = 0; i < 63; ++i)if (n >> i & 1)TOP = i;dfs(0, 0);return ANS;}int d[64][2];int dp(int p, bool need){if (p == -1)return need == 0;if (~d[p][need])return d[p][need];auto &rtn = d[p][need];rtn = 0;if (need){rtn += dp(p - 1, 1);if (~n >> p & 1)rtn += dp(p - 1, 0);}else{rtn += dp(p - 1, 0);if (n >> p & 1)rtn += dp(p - 1, 1);}return rtn;}LL DP(){MS(d, -1);TOP = -1;for (int i = 0; i < 63; ++i)if (n >> i & 1)TOP = i;return dp(TOP, 0);}int main(){table();while(~scanf("%lld", &n)){//printf("%lld\n", DFS());printf("%lld\n", DP());//printf("%lld\n", f[n]);}return 0;}/*【trick&&吐槽】2333 环境变了 可不要懵逼啊,沉着冷静才能想出正解~【题意】1 2 4 8 16 ... 这些2的幂次方价值的货币各有2个(这2个货币认定为相同的)问你让用这些数构成n(1e18)的方案数【分析】这里DP只能做到1e6爆搜大概可以做到1e15但是数位DP可以做到1e10000000我们用f[i][j]表示我们处理到第i位(比较低的第i位),之前高位是否需要一个数来补上的need = j的方案数可以做数位DP做转移就好啦——【时间复杂度&&优化】O(位数 * 2)*/


【2017年 腾讯校招笔试 B】【解方程 贪心 正难则反】每次操作加一或乘二,最小步数使得a变A且b变B

#include<stdio.h>#include<iostream>#include<string.h>#include<string>#include<ctype.h>#include<math.h>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }int casenum, casei;int a, b, A, B;int solve(int a, int b, int A, int B){if (a > A || b > B)return -1;if (a == b){if (A != B)return -1;//考虑从A到a的路径会更容易int ans = 0;while (A != a){++ans;if (A % 2 == 0 && A >= a * 2)A /= 2;else --A;}return ans;}else{int MU = (A - B);int ZI = (a - b);if (MU % ZI)return -1;int K = MU / ZI;int W = A - K * a;if (K <= 0 || W < 0)return -1;for (int k = K, cnt = 0; k; k >>= 1){cnt += k & 1;if (cnt > 1)return -1;}//接下来我们考虑如何走Bint ans = 0;for (int k = 2; k <= K; k *= 2){++ans;if (W & 1)++ans;W >>= 1;}return ans + W;}}int f[202][202];int DP(){MS(f, 63);f[a][b] = 0;for (int i = a; i <= A; ++i){for (int j = b; j <= B; ++j){gmin(f[i + 1][j + 1], f[i][j] + 1);gmin(f[i * 2][j * 2], f[i][j] + 1);}}if (f[A][B] == inf)f[A][B] = -1;return f[A][B];}int TOP = 100;int main(){//while(~scanf("%d%d%d%d", &a, &b, &A, &B))while (A = rand() % TOP + 1, B = rand() % TOP + 1, a = rand() % A + 1, b = rand() % B + 1, 1){int ans1, ans2;printf("%d\n", ans1 = solve(a, b, A, B));/*printf("%d\n", ans2 = DP());if (ans1 != ans2){while (1);}*/}return 0;}/*【trick&&吐槽】听说是topcoder的题,确实蛮难啊2333【题意】给你数字a与b和数字A与B我们每次可以同时操作a与b,使得数值+1,或者使得数值*2问你最少的操作步数,使得同时使得a变成A,以及b变成B【分析】我们假设最优路径是 (((加若干次)乘若干次)加若干次)乘若干次……这样子那么——我们可以把式子拆开:会发现,依然可以得到:A == a * K + W && B == b * K + W(如果有解的话)得到K = (A - B)/(a - b),W = A - a * K;当然,这里要先对a == b的情况做特判(详见代码)然后,我们得到了唯一的(K,W),应该有——A = K * a + WB = K * b + W虽然W可以被折叠起来,但是K是定的,于是K一定要是2^k才行,否则GG接下来所剩下的,是针对以求得的{K, W}求解。此时,不妨从低位向高位开始考虑——如果最低位是1,则表示现在还未进行的*2操作前,需要额外+1,否则,我们可以先*2,这个加1尽量推迟做。于是,我们一样可以在log的时间复杂度内,可以处理得到{K, W}的解(详见代码)【时间复杂度&&优化】O(logA)*/