列表

详情


NC51090. 新Nim游戏

描述

传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

输入描述

第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。

输出描述

输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。

示例1

输入:

6
5 5 6 6 5 5

输出:

21

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 3ms, 内存消耗: 504K, 提交时间: 2020-05-21 23:14:43

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N = 2e5 + 100;

int a[N], d[N];
int main()
{
    int n;
    cin >> n;//n堆火柴
    ll ans = 0;
    for (int i = 1; i <= n; i++) cin >> a[i], ans += a[i];//初始化并求出所有火柴的根数
    sort(a + 1, a + 1 + n, greater<int>());//按火柴的根数从大到小排列,采取贪心策略
    for (int i = 1; i <= n; i++)
    {
        int x = a[i];
        for (int i = 30; i >= 0; i--)//构造线性基
        {
            if (x >> i & 1)
            {
                if (d[i]) x ^= d[i];
                else
                {
                    d[i] = x;//将x插入到线性基集合中
                     break;
                }
            }
        }
        if (x) ans -= a[i];//a[i]在线性基集合里面,那么总和就减去a[i]
    }
    cout << ans << endl;
    return 0;
}

C++ 解法, 执行用时: 5ms, 内存消耗: 468K, 提交时间: 2022-04-21 17:38:49

#include<bits/stdc++.h>
using namespace std;
int n,a[101],d[31];
long long ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i];
	sort(a+1,a+n+1);
	for(int x=a[n];n;--n,x=a[n]){
		for(int j=30;j>=0;j--)if((x>>j)&1){if(d[j])x^=d[j];else{d[j]=x;break;}}
		if(!x)ans+=a[n];
	}
	cout<<ans;
	return 0;
}

上一题