2018年百度之星资格赛部分题解

44 篇文章 2 订阅

1001调查问卷

题解

由于每个问卷的问题只有两个选项,并且 m10 m ≤ 10 那么所有问题的状态为 210=1024 2 1 0 = 1024 种答案, n103 n ≤ 10 3 ,那么直接枚举保留问题的状态暴力计算就行

#include <bits/stdc++.h>

using namespace std;

char s[11];
int data[1100][11];
int n,T,m,k;
int num[1100];
int sum[1100];
int Get(int col,int bit)
{
    int numb = 0;

    int result = 0;
    while(bit)
    {
        if(bit %2)
        {
            result = result *2 + data[col][numb];
        }
        numb++;
        bit /=2;
    }
    return result;
}
int main()
{
    scanf("%d",&T);
    for(int Case = 1; Case <= T; Case ++)
    {
        scanf("%d %d %d",&n,&m,&k);
        for(int i = 0; i<n; i++)
        {
            scanf("%s",s);
            for(int j = 0; j<m; j++)
            {
                data[i][j] = s[j] == 'A'?1:0;
            }
        }
        int OutResult = 0;
        for(int i = 1; i<=((1<<m)-1); i++)
        {
            memset(num,0,sizeof(num));
            for(int j = 0; j<n; j++)
            {
                num[Get(j,i)]++;
            }
            sum[1024] = 0;
            for(int j = 1023; j >= 0; j--)
            {
                sum[j] = sum[j+1] + num[j];
            }
            int result = 0;
            for(int j = 0; j<1024; j++)
            {

                result = result+num[j]*sum[j+1];
            }
            if(result >=k)
                OutResult++;
        }
        printf("Case #%d: %d\n",Case,OutResult);

    }


    return 0;
}

1002 字串查询

题解

求解的是 [l,r] [ l , r ] 中最小非空子串的个数,那么最小非空子串肯定是在 [l,r] [ l , r ] 上长度为1的最小字母(思考?),那么我们只需要维持一个 n×26 n × 26 的前缀和数组 dp[i][j] d p [ i ] [ j ] 表示以i结尾的第j个字母的前缀和。每次查询都是遍历这个区间的26个字母,找最小的字母的个数就行

#include <bits/stdc++.h>

using namespace std;

const int Max = 110000;

int num[Max][26];

char str[Max];
int main()
{
    int T;
    int n,q;
    int l,r;
    scanf("%d",&T);
    for(int Case =1; Case<=T;Case++)
    {
        scanf("%d %d",&n,&q);
        scanf("%s",str);
        int len = strlen(str);
        for(int i = 0;i<26;i++) num[0][i] = 0;
        for(int i = 0;i<len;i++)
        {
            for(int j = 0;j<26;j++) num[i+1][j] = num[i][j];
            num[i+1][str[i]-'A'] ++;
        }
        printf("Case #%d:\n",Case);
        while(q--)
        {
            scanf("%d %d",&l,&r);
            int result = 0;
            for(int i = 0;i<26;i++)
            {
                result = num[r][i]-num[l-1][i];
                if(result)
                    break;
            }
            printf("%d\n",result);
        }

    }

    return 0;
}

1006 三原色图

题解

选出k个边使得权值和最小,并且满足边的限制条件。对于一个无向图,最小需要 n1 n − 1 条边才能使每一个连通,那么对于限制条件,我们可以分别去求一个最小生成树,将在生成树中的边进行标记,然后从小到大进行遍历虽有的边,经不在树中的边加入,然后将两种限制条件的结果取最小就是所要求的结果。

#include <bits/stdc++.h>

using namespace std;

const int INF = 0x3f3f3f3f;

struct node
{
    int u,v;
    int value,num;
    int flag;
} edge[1100];

int n,m;
int pre[110];
int resultR[1100],resultB[1100];
bool cmp(node a,node b)
{
    return a.value < b.value;
}

int Find(int x)
{
    return pre[x] == 0?x:pre[x] = Find(pre[x]);
}

void Union(int x,int y)
{
    int Fx = Find(x);
    int Fy = Find(y);
    if(Fx != Fy)
    {
        pre[Fx] = Fy;
    }
}
bool visR[1100],visB[1100];
int  Kuras(int limit,bool * vis)
{
    int ans = 0;
    int num = 0;
    memset(pre,0,sizeof(pre));
    for(int i = 0; i<m; i++)
    {
        if(edge[i].flag != limit && Find(edge[i].u) != Find(edge[i].v))
        {
            vis[edge[i].num] = true;
            ans += edge[i].value;
            Union(edge[i].u,edge[i].v);
            num++;
        }
        if(num == n-1)
        {
            return ans;
        }
    }
    ans = INF;
    return ans;
}
int main()
{
    int T;
    char s[2];
    scanf("%d",&T);
    for(int Case = 1; Case <= T; Case ++)
    {
        scanf("%d %d",&n,&m);
        for(int i = 0; i<m; i++)
        {
            scanf("%d %d %d %s",&edge[i].u,&edge[i].v,&edge[i].value,s);
            if(s[0] == 'R')
                edge[i].flag = 1;
            else if(s[0] == 'G')
                edge[i].flag = 2;
            else if(s[0] == 'B')
                edge[i].flag = 3;
        }
        sort(edge,edge+m,cmp);
        for(int i = 0; i<m; i++)
        {
            edge[i].num = i;
        }
        memset(visB,false,sizeof(visB));
        memset(visR,false,sizeof(visR));
        int ansR = Kuras(3,visR);
        int ansB = Kuras(1,visB);
        memset(resultR,INF,sizeof(resultR));
        memset(resultB,INF,sizeof(resultB));
        int k = n-1;
        resultR[k] = ansR;
        resultB[k] = ansB;
        for(int i = 0; i<m; i++)
        {
            if(!visR[i])
            {
                resultR[k+1] = min(resultR[k+1],resultR[k]+edge[i].value);
                k++;
            }

        }
        k = n-1;
        for(int i = 0; i<m; i++)
        {
           if(!visB[i])
            {
                resultB[k+1] = min(resultB[k+1],resultB[k]+edge[i].value);
                k++;
            }
        }

        printf("Case #%d:\n",Case);
        for(int i = 1; i<=m; i++)
        {
            int re = min(resultR[i],resultB[i]);

            if(re >= INF)
                printf("-1\n");
            else
                printf("%d\n",re);
        }
    }

    return 0;
}

其余的题大家可以参考官方题解,博主会在空闲时间将题补了

补题

1005 序列计数 HDU6348

从小到大计算子序列的长度,可以根据长度小的推长度大的序列的个数,使用树状数组来优化,由于期望的最长上升子序列并不大,可以暴力一下o( ̄▽ ̄)o

#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <iostream>

using namespace std;

const int Max = 1e4+100;
const int Mod = 1e9+7;

int num[Max];
int tree[Max*4];
int arr[Max];
int n;
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int v)
{
    while(x<n)
    {
        tree[x]  = (tree[x] +v)%Mod;
        x += lowbit(x);
    }

}
int Query(int x)
{
    long long ans = 0;
    while(x)
    {
        ans = (ans + tree[x])%Mod;
        x-=lowbit(x);
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int Case = 1; Case <= T; Case ++)
    {
        scanf("%d",&n);
        for(int i = 0;i<n;i++)
        {
            scanf("%d",&arr[i]);
            num[i] = 1;
        }
        int len = n;
        long long result = n;
        printf("Case #%d:",Case);
        while(result)
        {
            printf(" %lld",(result%Mod));
            result =0;
            len --;
            memset(tree,0,sizeof(tree));
            for(int i = 0;i<n;i++)
            {
                add(arr[i],num[i]);
                num[i] = Query(arr[i]-1);
                result = (result + num[i])%Mod;
            }
        }
        while(len--) printf(" 0");
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值