2018.5.19——第九届acm山东省省赛部分题解E+G

本文解析了两道经典的算法竞赛题目:一是通过维护序列中元素的最值来确定移除哪个元素可最大化“好”元素数量;二是基于Nim博弈原理的石头游戏,通过动态规划计算Bob能确保胜利的不同开局方式的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E:sequence

题目描述

  We define an element  in a sequence "good", if and only if there exists  (1≤j<i) such that .    Given a permutation p of integers from 1 to n. Remove an element from the permutation such that the number of "good" elements is maximized.
输入描述:
The input consists of several test cases. The firstline of the input gives the number of test cases,.For each test case, the first line contains aninteger ,representing the length of the given permutation.The second line contains n integersp1,p2,…,pn ,representing the given permutation p.It's guaranteed that  .
输出描述:
For each test case, output one integer in a singleline, representing the element that should be deleted. If there are severalanswers, output the minimal one.
示例1
输入
2
1
1
5
5 1 2 3 4
输出
1

5

这道题的思路是维护一个到第i个点之前的最小值first[i]和到第i个点之前的次最小值secend[i],然后再循环判断i是否是good数,并进行标志,然后再一重循环,假设这个点被去掉了,会影响几个good值,用cnt函数进行记录,然后再进行判断,如果这个数不是good值并且cnt[i]==0,说明去掉这个数对good值无影响,那么就可以直接找出这样的代表数字最小的进行输出;如果没有这样的数字,那么我们就要着手于good值,因为good值对good数的影响只能是一,或者找非good数,并且他的cnt等于一,这样的数对good值也是一。

所以很多细节方面的需要考虑。比赛的时候我们没有想出来,可能是思维还是需要训练的。这道题就是一道需要考虑很多细节的思维题。

代码:

#include<bits/stdc++.h>
using namespace std;
int first[10000010];
int second[10000010];
int a[10000010];
int flag[10000010];
int cnt[10000010];
const int maxn=0x3f3f3f3f;
int ans;
int main()
{
    int t;
    int n;
    int k;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int now_min=maxn;
        int last_min=maxn;
        for(int i=1;i<=n;i++){
            first[i]=now_min;
            second[i]=last_min;

            if(a[i]<=now_min){
                last_min=now_min;
                now_min=a[i];
               // flag[i]=1;
            }
            else if(a[i]<last_min){
                last_min=a[i];
            }
        }//维护最大值和次小值,jia标记,bad数标记为1
        for(int i=1;i<=n;i++)
        {//不好数标记为1,好数标记为0
            if(a[i]<=first[i])
                flag[i]=1;
            else
                flag[i]=0;
        }
        for(int i=1;i<=n;i++){
            if(flag[i]==1){//是
                k=i;
                cnt[k]=0;
            }
            else{
                if(a[i]<=second[i])
                    cnt[k]++;
            }
        }
       ans=maxn;
        for(int i=1;i<=n;i++){
            if(flag[i]==1&&cnt[i]==0)
                ans=min(ans,a[i]);
        }
        if(ans==maxn) 
        {
            for(int i=1;i<=n;i++)
            {
              if(flag[i]==0) 
                ans=min(ans,a[i]);
              else if(cnt[i]==1)
                ans=min(ans,a[i]);
            }
        }
        printf("%d\n",ans);
    }
}

G:

games

Alice and Bob are playing a stone game. There are n piles of stones. In each turn, a player can remove some stones from a pile (the number must be positive and not greater than the number of remaining stones in the pile). One player wins if he or she remove the last stone and all piles are empty. Alice plays first.    To make this game even more interesting, they add a new rule: Bob can choose some piles and remove entire of them before the game starts. The number of removed piles is a nonnegative integer, and not greater than a given number d. Note d can be greater than n, and in that case you can remove all of the piles.    Let ans denote the different ways of removing piles such that Bob are able to win the game if both of the players play optimally. Bob wants you to calculate the remainder of ans divided by109+7.
输入描述:
The first line contains an integer T, representingthe number of test cases.For each test cases, the first line are twointegers n and d, which are described above.The second line are n positive integersai,representing the number of stones in each pile.
输出描述:
For each test case, output one integer (modulo109+7.) in asingle line, representing the number of different ways of removing piles thatBob can ensure his victory.
示例1
输入

2
5 2
1 1 2 3 4
6 3
1 2 4 7 1 2
输出

2
5

这道题显然是nim博弈,结论就是异或值之和为0的先手输。所以有了这个结论就可以写了。

比赛时学长写了一发秒过,现在想来好骄傲啊。赛后重现赛自己写,写出了好多bug,WA了好多发。自己定义的longlong数组一直数组越界,好久才改的int。。还有最后mod的时候,结果要再一次mod啊,老是犯这种错误。

就是用dp[i][j][k]表示前i为选j个,异或值为k的种类数目。

状态转移方程为:

dp[i][j][k]=(dp[i-1][j][k^a[i]]%mod+dp[i-1][j-1][k]%mod)%mod;

代码:

#include<bits/stdc++.h>
using namespace std;
int a[1005];
int dp[1005][15][1030];
const int mod=1000000007;
int main()
{
    int t;
    scanf("%d",&t);
    int n,d;
    while(t--)
    {
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    memset(dp,0,sizeof(dp));
    dp[1][0][a[1]]=1;
    dp[1][1][0]=1;//前i个删除j个异或和为k
    long long ans=0;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=min(n,min(i,d));j++){
                for(int k=0;k<=1024;k++){
        dp[i][j][k]=(dp[i-1][j][k^a[i]]%mod+dp[i-1][j-1][k]%mod)%mod;
                     //cout<<"$$$$$$$$$$"<<dp[i][j][k]<<endl;
                }
        }
    }
    for(int i=0;i<=min(d,n);i++){
        //cout<<dp[n][i][0]<<"*&*&*&*"<<endl;
        ans=(ans%mod+dp[n][i][0]%mod)%mod;
    }
    printf("%lld\n",ans);
        //cout<<ans<<endl;
    }
    return 0;
}

还有F题容斥的题还没有看。

ps:重现赛的时候真的好想念学长大佬啊。感觉闭着眼只用读题的感觉真好。好希望可以成为像他们那么厉害的努力的人。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值