牛客练习赛41(A,B,C,D)

A.知识点:博弈论

题解思路:

第一种情况,n=m,显然“Yes”

第二种情况,n!=m;把n分为就奇偶两种情况:(1)n为奇数,要将所有硬币全部变成反面,等价于所有的回合数翻硬币的次数之和是奇数,那么即使Bob什么都不做,Alice无论怎么做,都不会达到目的 (2)n为偶数,等价于总数和为偶数,那么,Alice有可能完成,这时候,Bob只要在第一次把任意一个硬币翻过来,这个时候的情况就和(1)一样了。

所以,n=m,yes

n!=m,no

 

B.知识点:动态规划

dp[i][j]表示在第i个回合,构成数字i的情况数。

由此,状态转移方程就是:dp[i] [j] = dp[i-1] [j-a[i] ] +dp[i-1] [-j]

注意要加两个东西:

(1):滚动数组 (二维存不下)

(2)排除所有j="666"的情况

 

由于j和-j以及j-a[i]的正负的不确定性,只用一个dp数组滚动肯定不行,因此要加上一个辅助数组来保存上一个回合dp值

code如下:

#include<bits/stdc++.h>
#define register int rint
#define INF 0x3f3f3f3f3f
#define MOD 100000007
#define mem(a,b) memset(a,b,sizeof(a))
#define PI 3.141592653589793
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=400000;
const int Max=1e4+20;
const double esp=1e-8;
inline int rd() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int n,a[N],dp[N+200],tmp[N+200];
int main()
{
    n=rd();
    for(int i=1;i<=n;++i)   a[i]=rd();
    mem(dp,0);

    dp[199800]=1;
    for(int i=1;i<=n;++i)
    {
        mem(tmp,0);
        for(int j=0;j<=399600;++j)
        {
            int real=j-199800;
            if(real==666)   continue;
            tmp[j]=(dp[399600-j]+tmp[j])%MOD;
            tmp[j]=(dp[j-a[i]]+tmp[j])%MOD;
        }
        //cout<<tmp[19934]<<' '<<tmp[199467]<<' '<<tmp[199800]<<' '<<tmp[200133]<<' '<<tmp[200466]<<endl;
        memcpy(dp,tmp,sizeof(tmp));
    }

    cout<<dp[199134]<<endl;
    //cout<<dp[1][n][666]<<endl;
}

 

C.知识点:并查集

1.把所有节点变成并查集森林

2.把每一棵树的a【i】总和求出+排序

3.加和前m个,即为所求

 

D.知识点:思维+bfs

题解思路:有n个数,每个数m位,那么最多有2^m种情况,每一种遍历再和n个数异或的话,时间复杂度就是n*2^m,太大了。

让我们换一种思路来看,把每一位是0还是1看成一种状态,那么就有m种状态,现在把这m个位想象成一个图,起点是数字x,终点可能是(1111...)m个1(可能不到)。那么我们每次改变一个位,然后标记我们走过的路径的长度,这样就类似于bfs的做法(一边标记,一边拓展)。最总我们用这n个数能走过的最大路径长度*-1+m就是答案了。

 

code(来自官方题解:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40380369)

#include<bits/stdc++.h>
using namespace std;
int deep[1<<22];
void solve(){
    queue<int> q;
    int n,m,ans=0;
    char s[30];
    scanf("%d %d",&n,&m);
    memset(deep,-1,sizeof(int)*(1<<(m+1)));
    for(int i=0;i<n;i++){
        scanf("%s",s);
        int f=0;
        for(int j=0;j<m;j++){
            f+=(1<<j)*(s[j]-'0');
        }
        if(deep[f]==-1) {
            deep[f]=0;
            q.push(f);
        }
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        ans=max(ans,deep[u]);
        for(int i=0;i<m;i++){
            if(deep[u^(1<<i)]==-1){
                deep[u^(1<<i)]=deep[u]+1;
                q.push(u^(1<<i));
            }
        }
    }
    printf("%d\n",m-ans);
}
int main()
{
    solve();
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值