首页 > 试题广场 >

【2021】360编程题-神枪手

[编程题]【2021】360编程题-神枪手
  • 热度指数:1140 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

小马最近找到了一款打气球的游戏。

每一回合都会有 n个气球,每个气球都有对应的分值,第 i个气球的分值为ai

这一回合内,会给小马两发子弹,但是由于小马的枪法不准,一发子弹最多只能打破一个气球,甚至小马可能一个气球都打不中。

现给出小马的得分规则:

1. 若小马一只气球都没打中,记小马得0分。

2. 若小马打中了第 i只气球,记小马得ai 分。

3. 若小马打中了第 i只气球和第 j只气球(i<j),记小马得 ai|aj分。

(其中| 代表按位或,按位或的规则如下:

参加运算的两个数,按二进制位进行或运算,只要两个数中的一个为1,结果就为1。
即 0|0=0, 1|0=1, 0|1=1, 1|1=1。
例 2|4即 00000010|00000100=00000110,所以2|4=6

现在请你计算所有情况下小马的得分之和。


输入描述:

第一行,一个整数n,表示此回合的气球数量。

第二行,用空格分开的n 个整数,第i个整数为ai,表示每个气球对应的分值。

对于其中60% 的数据, 1≤n≤1000, 1≤ai≤100000

对于另外 40% 的数据,  1≤n≤50000, 1≤ai≤100000



输出描述:

一行一个整数,代表所有情况下小马的得分之和。

示例1

输入

3 
1 2 3

输出

15

说明

这个题暴力法用缓冲流超时,用扫描器Scanner竟然过了
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] scores = new int[n];
        long totalScore = 0;
        for(int i = 0; i < n; i++) {
            scores[i] = sc.nextInt();
            totalScore += scores[i];
        }
        for(int i = 0; i < n; i++) {
            for(int j = i + 1; j < n; j++)
                totalScore += (scores[i] | scores[j]);
        }
        System.out.println(totalScore);
    }
}

发表于 2021-08-23 13:28:07 回复(1)
很巧妙的算贡献
处理每一位出现了几次
比如样例 
3
1 2 3

先转化数组中的数字:1 -> 001  2-> 010  3->011
处理出一个计数数组代表每一位的 '1' 的出现次数:bits = [2, 2, 0]
从 bits[0] = 2 来看,第0个二进制位出现了 2 次 1,其实就是 1 和 3 对这个二进制位做出了贡献。

由于或运算的性质是“任意为 1 则为 1”,因此:
如果命中一次,其实就是数组求和,不多说。
如果命中两次,则会有以下可能:
可能性一:一次命中有贡献,一次命中没有贡献,那么其实就是 C(bits[i], 1) * C(n-bits[i], 1) ,代表从有贡献的可能性中选一个击中,从没有贡献的可能性中也选一个击中。
可能性二:两次命中全有贡献,相当于从1 和 3 中选两个击中,那么其实就是 C(bits[i], 2),代表从所有有贡献的可能性中选两次命中。
把某一位的贡献算出后,再乘以这一位的权值 2i (2 的 i 次方) ,就是这一位对答案的总贡献。
求和全部二进制位的总贡献就是答案。

#include <stdio.h>
#include <string.h>
#include <math.h>

int n, m, bits[32];
long long ans = 0;
int main() {
    memset(bits, 0, sizeof bits);
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &m);
        ans += m;
        int j = 0;
        while (m) {
            bits[j] += m % 2;
            m /= 2;
            j++;
        }
    }
    for (int i = 0; i < 20; i++) {
        long long cnt = bits[i] * (n - bits[i]);
        cnt += bits[i] * (bits[i] - 1) / 2;
        ans += pow(2, i) * cnt;
    }
    printf("%lld\n", ans);

    return 0;
}


编辑于 2021-09-02 14:19:00 回复(0)
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,a[50005],s[30];
ll ans,sum,pw[30];
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j;
    pw[0]=1;
    for(i=1;i<=20;i++) /**< 预处理2的i次幂 */
        pw[i]=pw[i-1]*2;
    cin>>n;
    for(i=1;i<=n;i++) /**< 一发命中直接求和 */
        cin>>a[i],ans+=a[i];
    for(i=1;i<=n;i++)
    {
        ans+=sum;/**< 任意两数或的值如何求?首先两数或不会比任意一数小,
        所以a[i]和a[1]到a[i-1]做或运算首先加上a[1]到a[i-1]的总和*/
        sum+=a[i];
        j=0;
        while(a[i])
        { /**< a[i]的二进制位中某一位为1时,与前面所有数字中这一位没有1的或运算,属于额外所得。 */
            if(a[i]%2==1) /**< s[]数组存储前i-1个数字,各位二进制中1的个数 */
                ans+=pw[j]*(i-1-s[j]),s[j]++;
            j++;
            a[i]/=2;
        }
    }
    cout<<ans;
    return 0;
}

发表于 2021-08-14 11:52:46 回复(0)
import math
bits=[0]*32 #32Bits
for num in nums:
    num_b_str = list(bin(num)[2:])[::-1]
    for i in range(len(num_b_str)):
        if num_b_str[i]=='1':
            bits[i]+=1
res=sum(nums)
for i in range(len(bits)):
    bit=bits[i]
    cns = math.comb(bit,1)*math.comb(N-bit,1)
    cns+=math.comb(bit,2)
    res+=pow(2,i)*cns
print(res)
谢谢1楼!附上python版本
发表于 2022-09-10 02:57:18 回复(0)
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define ll long long


int main() {
    int n, temp;
    cin >> n;
    ll sum=0;
    vector<int> vec;
    for (int i = 0; i < n; i++) {
        cin >> temp;
        vec.push_back(temp);
    }
    for(int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            sum += vec[i] | vec[j];
        }
    }
    cout << sum << endl;
    return 0;
}

发表于 2022-09-09 15:24:00 回复(0)

别问问就暴力
#include<iostream>
#include<vector>
#include<map>
using namespace std ;


int main()
{
    long long x ;
   
    vector< long long> arr;
    cin >>x ;
     long long sum = 0 ;
    for(int i =0 ; i <x ; i ++)
    {
         long long temp ;
        cin>>temp;
        sum +=temp;
        
        arr.push_back(temp);
    }
     //cout<<sum<<endl;
   // for(map<int,int>::iterator it = arr1.begin(); it!= arr1.end(); it++)

   
    for( long long i =0 ; i <arr.size() ; i ++)
    {
        for( long long j =i+1 ; j <arr.size() ; j ++)
        {
            sum +=(arr[i]|arr[j]);
         //  cout<<arr[i]<<arr[j]<<endl;
        }
        // cout<<sum<<endl;
    }   
    cout<<sum<<endl;
    return 0;
}
发表于 2022-04-20 21:46:48 回复(0)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, a;
    cin >> n;
    //统计每个二进制位上0和1各有多少
    vector<vector<int>> b(31,vector<int>(2,0));
    long long sum = 0;
    for (int i = 0; i < n; ++i) {
        cin >> a;
        for (int i = 0; i < 31; ++i) {
            b[i][(a >> i) & 1] += 1;
        }
        sum += a;
    }
    long long inc = 0;
    for (int i = 0; i < 31; ++i) {
        //0与任意1异或得1 算清楚了0和1之间异或的情况
        //1和1异或 单方向 不要重复计算
        int n = b[i][1];
        //累加的两部分分别表示0和1异或的和、1和1异或的和
        inc += 1LL * b[i][0] * b[i][1] * (1 << i) + 1LL * (n*(n-1)/2)* (1<<i);
    }
    //加上有一枪没种的情况
    inc += sum;
    cout<<inc<<endl;
    return 0;
}

发表于 2021-06-07 11:40:49 回复(0)
python 暴力法超时,使用位运算做的,其中还有些问题需要注意的问题,大家自己看代码吧
balls =int(input().strip())
data =list(map(int, input().strip().split()))
res =sum(data)
fori inrange(32):
    temp =0
    forc indata:
        if(c >> i) & 1==1:
            temp +=1
    iftemp <=1:
        res +=2**i *(temp *(balls -temp))
    else:
        res +=2**i *(temp *(balls -temp) +(temp *(temp -1)) /2)
print(int(res))
发表于 2021-05-29 12:55:13 回复(0)

显然答案为:

(1). 打破一个气球
(2). 所有数对进行或运算后的结果之和

二者之和。

(1) 的结果显然就是所有气球分值之和 sum

所以关键在于如何计算所有数对进行或运算后的结果和。

或运算 a | b 的结果不会减少 b 的值,而只是会在 b 的基础上,加上二进制表示中某位上 a1b0 的该位的权重。

其中

所以计算出每位上为 0 的数量和为 1 的数量,然后相乘的结果乘以位权就是或运算增加的值 inc

过程中因为每个位置的数重复计算了一次,所以结果为 (inc + sum * n - sum)/2

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    int n, a;
    cin >> n;
    vector<array<int, 2>> b(31);
    long long sum = 0;
    for (int _ = 0; _ < n; ++_) {
        cin >> a;
        for (int i = 0; i < 31; ++i) {
            b[i][a >> i & 1] += 1;
        }
        sum += a;
    }
    long long inc = 0;
    for (int i = 0; i < 31; ++i) {
        inc += 1LL * b[i][0] * b[i][1] * (1 << i);
    }
    cout << (inc + sum * n - sum) / 2 + sum << '\n';
    return 0;
}
发表于 2021-05-14 22:12:06 回复(0)
个人觉得有其他优秀的解法但是就是暴力A了
#include<bits/stdc++.h>

using namespace std;

const int N=1e5+100;
#define ll long long
ll a[N];

int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans+=a[i];
        for(int j=i+1;j<=n;j++) ans+=a[i]|a[j];
    }
    cout<<ans<<endl;
    return 0;
}


发表于 2021-05-10 23:55:17 回复(1)
package test4;

import java.util.Scanner;

/**
 * @author xq
 * @create 2021-05-07-21:10
 */
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        int K = scanner.nextInt();
        int[][] ints = new int[N][2];
        int sum = 0;
        for (int i = 0; i < N; i++) {
            ints[i][0] = scanner.nextInt();
            ints[i][1] = scanner.nextInt();
            sum+=ints[i][1];
        }
        if (K>=N){
            System.out.println(sum);
            System.exit(0);
        }
        int[][] dp = new int[N][K+1];
        dp[0][0] = ints[0][1];
        int max = 0;
        for (int i = 1; i <= K; i++) {
            for (int j = 0; j < N; j++) {
                if (dp[j][i-1]>0){
                    for (int k = j+1; k < N; k++) {
                        if (ints[k][0]-ints[j][0]<=M){
                            dp[k][i] = Math.max(dp[k][i],dp[j][i-1]+ints[k][1]);
                            max = Math.max(dp[k][i],max);
                        }
                    }
                }
            }
        }
        System.out.println(max);
    }
//    public static void main(String[] args) {
//        long sum = 0;
//        Scanner scanner = new Scanner(System.in);
//        while (scanner.hasNext()){
//            int nextInt = scanner.nextInt();
//            int[] ints = new int[nextInt];
//            for (int i = 0; i < nextInt; i++) {
//                ints[i] = scanner.nextInt();
//                sum+=ints[i];
//            }
//            for (int i = 0; i < nextInt; i++) {
//                for (int j = i+1; j < nextInt; j++) {
//                    int temp = ints[i]|ints[j];
//                    sum+=temp;
//                }
//            }
//        }
//        System.out.println(sum);
//    }
}

发表于 2021-05-08 09:44:45 回复(0)