思维题练习部分

附注: 今天被降维打击了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:
3
10
20
30
输出样例1:
YES
输入样例2:
3
10
10
10
输出样例2:
NO
输入样例3:
3
120
120
120
输出样例3:
YES

没什么好说的,直接暴力

思路一:dfs

#include <bits/stdc++.h>
#define int long long 
using namespace std;
const int N = 510;
int n;
int a[N];
int f[N][N];
bool dfs(int u,int sum)
{
    if(u>n)return sum%360==0;
    if(dfs(u+1,sum+a[u])|dfs(u+1,sum-a[u]))return true;
}
signed main()
{
    cin>>n;
    for (int i = 1; i <= n; i ++ )cin>>a[i];
    if(dfs(1,0))puts("YES");
    else puts("NO");
    return 0;
}

思路二: 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];

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

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 500,mod = 360;
int n;
int a[N];
int f[N][N];
signed main()
{
    cin>>n;
    for (int i = 1; i <= n; i ++ )cin>>a[i];
    f[0][0]=1;
    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= 360; j ++ )
        {
            f[i][j]=f[i-1][(j+a[i])%mod];
            f[i][j]+=f[i-1][(j-a[i]+mod)%mod];
        }
    if(f[n][0])puts("YES");
    else puts("NO");
    return 0;
}

2.相遇问题

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

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

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

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

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

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

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

输入格式

第一行包含一个整数 a。

第二行包含一个整数 b。

输出格式

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

数据范围

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

输入样例1:
3
4
输出样例1:
1
样例1解释
在本样例中,让第一个人上一级台阶或第二个人下一级台阶均可,消耗总体力为 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)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5;
int a,b;
int s[N];
int main()
{
    cin>>a>>b;
    int res = 0;
    for (int i = 1; i <= 1000; i ++ )
        s[i]=s[i-1]+i;
    int r1 = abs(b - a)/2,r2 = abs(b-a)-abs(b - a)/2;
    res=s[r1]+s[r2];
    cout << res <<'\n';
    return 0;
}


//也可以这样写
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int a, b;
    cin >> a >> b;
    int n = abs(a - b);
    int x = n / 2, y = n - x;
    cout << x * (x + 1) / 2 + y * (y + 1) / 2 << endl;

    return 0;
}

3.砝码称重

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

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

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

共一行,一个整数 n。

输出格式

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

数据范围

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

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

输入样例1:

6
输出样例1:

3
样例1解释

在此样例中,我们只需要选取重量为 1,2,3 的砝码各一个,即可组成 1∼6 之间的任意整数重量:

1=1
2=2
3=3
4=1+3
5=2+3
6=1+2+3
所以最少需要 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

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
signed main()
{
    cin>>n;
    int res = 0;
    while((1 << res ) - 1 < n)res++;
    cout << res <<'\n';
    return 0;
}

//这样写也行
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin >> n;
    int res = 0;
    while(n)
    {
        n /= 2;
        res ++;
    }
    cout << res << "\n";
    return 0;
}

//当然大家会发现,log以2为底的数也可以做
#include <bits/stdc++.h>
using namespace std ;
int main () {
    int n ;
    cin >> n ;
    cout << (int)(log2 (n)) + 1 ;
}

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

嗷嗷嗷~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值