• 思维题练习部分


    附注: 今天被降维打击了hh,被虐的滋味真不爽,orz

    1.指针

    初始时,量角器上的指针指向刻度 0。

    现在,请你对指针进行 n 次拨动操作,每次操作给定一个拨动角度 ai,由你将指针拨动 ai 度,每次的拨动方向(顺时针或逆时针)由你自由决定。

    请你判断,能否通过合理选择每次拨动的方向,使得指针最终仍然指向刻度 0。
    输入格式
    第一行包含整数 n。

    接下来 n 行,每行包含一个整数 ai,表示一次操作的拨动角度。

    输出格式
    如果可以做到指针最终仍然指向刻度 0,则输出 YES,否则输出 NO。

    数据范围
    前 4 个测试点满足 1≤n≤3。
    所有测试点满足 1≤n≤15,1≤ai≤180。

    1. 输入样例1
    2. 3
    3. 10
    4. 20
    5. 30
    6. 输出样例1
    7. YES
    8. 输入样例2
    9. 3
    10. 10
    11. 10
    12. 10
    13. 输出样例2
    14. NO
    15. 输入样例3
    16. 3
    17. 120
    18. 120
    19. 120
    20. 输出样例3
    21. YES

    没什么好说的,直接暴力

    思路一:dfs

    1. #include
    2. #define int long long
    3. using namespace std;
    4. const int N = 510;
    5. int n;
    6. int a[N];
    7. int f[N][N];
    8. bool dfs(int u,int sum)
    9. {
    10. if(u>n)return sum%360==0;
    11. if(dfs(u+1,sum+a[u])|dfs(u+1,sum-a[u]))return true;
    12. }
    13. signed main()
    14. {
    15. cin>>n;
    16. for (int i = 1; i <= n; i ++ )cin>>a[i];
    17. if(dfs(1,0))puts("YES");
    18. else puts("NO");
    19. return 0;
    20. }

    思路二: dp

    定义f[i][j]为从前i个物品里,角度为j的方案数

    方程:

    f[i][j]=f[i-1][(j+a[i])%360]

    f[i][j]+=f[i][(j-a[i]+360)m%360];

    这里只需要判断是不是即可,可以用位运算符代替

    1. #include
    2. using namespace std;
    3. #define int long long
    4. const int N = 500,mod = 360;
    5. int n;
    6. int a[N];
    7. int f[N][N];
    8. signed main()
    9. {
    10. cin>>n;
    11. for (int i = 1; i <= n; i ++ )cin>>a[i];
    12. f[0][0]=1;
    13. for (int i = 1; i <= n; i ++ )
    14. for (int j = 0; j <= 360; j ++ )
    15. {
    16. f[i][j]=f[i-1][(j+a[i])%mod];
    17. f[i][j]+=f[i-1][(j-a[i]+mod)%mod];
    18. }
    19. if(f[n][0])puts("YES");
    20. else puts("NO");
    21. return 0;
    22. }

    2.相遇问题

    一个无限长的楼梯上站着两个人,其中一个人在第 aa 级台阶上,另一个人在第 bb 级台阶上。

    两个人都可以自由的上下移动,每人每次可以向上或向下移动一级台阶。

    每个人的每次移动都要消耗体力,具体为:

    对于同一个人来说,其第 1次移动消耗的体力为 1,第 2 次移动消耗的体力为 2,第 3 次移动消耗的体力为 3,以此类推。

    例如,如果一个人先向上移动一级台阶,再向下移动一级台阶,最后再次向上移动一级台阶,那么他消耗的总体力值为 1+2+3=6。

    两个人想要通过合理移动,使得他们能够在同一级台阶上相遇,并且相遇时,两人消耗的总体力值之和尽可能小。

    请你计算,两人消耗的总体力值之和的最小可能值。

    输入格式

    第一行包含一个整数 a。

    第二行包含一个整数 b。

    输出格式

    一个整数,表示两人消耗的总体力值之和的最小可能值。

    数据范围

    所有测试点满足,1≤a,b≤1000,a≠b。

    1. 输入样例1
    2. 3
    3. 4
    4. 输出样例1
    5. 1
    6. 样例1解释
    7. 在本样例中,让第一个人上一级台阶或第二个人下一级台阶均可,消耗总体力为 1

    思路:我们可以发现,每一次移动消耗的体力都比上一次要多,所以我们可以考虑贪心来得到最优解。经过分析,我们发现,这样的策略可以得到最优解:A移动一步,B移动一步,A移动一步,B移动一步.....以此类推。消耗的体力为:1,1,2,2,3,3,4,4.....这样可以保证体力消耗得更少。

    我们可以先算出两人的距离(也就是abs(a-b)),然后除以2让A走一半B走另一半。接着,我们用等差求和公式(首项+末项)*项数/2求出路程,再乘2就是他们俩的路程和。需要注意的是,当两人的距离为奇数的时候,需要其中一个人再移动一步,所以当距离为奇数的时候我们应该再加上abs(a-b)/2+1。

    当然,我用的是前缀和处理

    时间复杂度 O(1)

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N = 1e5;
    6. int a,b;
    7. int s[N];
    8. int main()
    9. {
    10. cin>>a>>b;
    11. int res = 0;
    12. for (int i = 1; i <= 1000; i ++ )
    13. s[i]=s[i-1]+i;
    14. int r1 = abs(b - a)/2,r2 = abs(b-a)-abs(b - a)/2;
    15. res=s[r1]+s[r2];
    16. cout << res <<'\n';
    17. return 0;
    18. }
    19. //也可以这样写
    20. #include
    21. #include
    22. #include
    23. using namespace std;
    24. int main()
    25. {
    26. int a, b;
    27. cin >> a >> b;
    28. int n = abs(a - b);
    29. int x = n / 2, y = n - x;
    30. cout << x * (x + 1) / 2 + y * (y + 1) / 2 << endl;
    31. return 0;
    32. }

    3.砝码称重

    砝码是一种作为质量标准的物体,通常为金属块或金属片,可以用作称量较精准的质量。

    给定一个整数 nn,我们需要选取一些砝码用于称量,砝码只能放在一边,要求:

    所有选取砝码的总重量恰好为 n。
    每个选取砝码的重量 x 都是满足 1≤x≤n的正整数。
    可以选取多个重量相同的砝码,例如选取两个重量为 1 的砝码。
    利用选取的砝码(部分或全部),可以组成 1∼n之间的任意整数重量。
    选取砝码的数量应尽可能少。
    请你计算并输出选取砝码的最少可能数量。
    输入格式

    共一行,一个整数 n。

    输出格式

    一个整数,表示选取砝码的最少可能数量。

    数据范围

    前三个测试点满足 1≤n≤10。

    所有测试点满足 1≤n≤109。

    1. 输入样例1
    2. 6
    3. 输出样例1
    4. 3
    5. 样例1解释
    6. 在此样例中,我们只需要选取重量为 1,2,3 的砝码各一个,即可组成 16 之间的任意整数重量:
    7. 1=1
    8. 2=2
    9. 3=3
    10. 4=1+3
    11. 5=2+3
    12. 6=1+2+3
    13. 所以最少需要 3 个砝码。

    有点像蓝桥里的一道题,思想类似

    小学奥数里有一道题类似与本题,结论是取1,2,4,8,......的时候能组成的数字最多

    思路: 数字选择 1,2 ,4.....2^(k)       2^(k+1)

    如果下一个数字小于2^(k+1)那么能表示的数字比2^(k+1)小,不妨用2^(k+1)代替

    能表示的数的范围是1~2(k)-1

    1. #include
    2. #define int long long
    3. using namespace std;
    4. int n;
    5. signed main()
    6. {
    7. cin>>n;
    8. int res = 0;
    9. while((1 << res ) - 1 < n)res++;
    10. cout << res <<'\n';
    11. return 0;
    12. }
    13. //这样写也行
    14. #include
    15. using namespace std;
    16. int main()
    17. {
    18. int n;
    19. cin >> n;
    20. int res = 0;
    21. while(n)
    22. {
    23. n /= 2;
    24. res ++;
    25. }
    26. cout << res << "\n";
    27. return 0;
    28. }
    29. //当然大家会发现,log以2为底的数也可以做
    30. #include
    31. using namespace std ;
    32. int main () {
    33. int n ;
    34. cin >> n ;
    35. cout << (int)(log2 (n)) + 1 ;
    36. }

    其他的改天再补吧,晚安,玛卡巴卡

    嗷嗷嗷~

  • 相关阅读:
    串口通信问题排查总结
    【花雕动手做】有趣好玩的音乐可视化系列小项目(17)--光导纤维灯
    知识库系统推荐,强大的全文检索与文档分类管理功能
    Vulkan-着色器及编译SPIR-V
    TIA博途中累计流量的两种计算方法示例
    线代9讲 第6讲
    Java抽象类和接口
    CF - B. Combinatorics Homework(思维)
    【Linux】云服务器的购买与Linux远程连接
    Spring Gateway 网关常见配置说明
  • 原文地址:https://blog.csdn.net/weixin_55296581/article/details/126337839