Google Kick Start 2020 Round A

Allocation

题意

N个房子出售,每个卖Ai刀,现有B刀资金,求最多买多少个。

思路

贪心,排序后从小到大买

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;

int a[MAX];

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n,b,res=0;
        scanf("%d%d",&n,&b);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        sort(a,a+n);
        for(int i=0;i<n;i++)
        {
            if(a[i]>b)break;
            b-=a[i];
            res++;
        }
        printf("Case #%d: %d\n",++cas,res);
    }
}

Plates

题意

n摞盘子,每一摞有k个,每个盘子有一定价值,现在要选p个盘子,要求每次只能选一摞的从上到下的一部分,求如何选使得总价值最大

思路

DPdp[i][j] 表示前i摞中共选j个可得的最大价值,转移枚举当前摞选m个(选前缀),然后由 dp[i-1][j-m] 转移而来

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=1505;

int a[MAX][MAX],pre[MAX][MAX],dp[MAX][MAX];

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n,p,m;
        scanf("%d%d%d",&n,&m,&p);
        memset(dp,0,sizeof dp);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&a[i][j]);
                pre[i][j]=pre[i][j-1]+a[i][j];
            }
        for(int i=1; i<=n; i++)
            for(int j=0; j<=min(i*m,p); j++)
                for(int k=0; k<=min(j,m);k++)
                    dp[i][j]=max(dp[i][j],dp[i-1][j-k]+pre[i][k]);
        printf("Case #%d: %d\n",++cas,dp[n][p]);
    }
}

Workout

题意

给一个单调递增序列,有n个数,设D值为此序列每个数与相邻数的差的最大值,现在可以在保证序列单调增的情况下,在此序列任意位置插入任意大小的k个数,求如何使得此序列D值最小,求出最小D值。

思路

容易想到,可以在两个数中间插a个数,使得两者差变为原来的a分之一(向上取整),先处理出原序列所有差,然后二分答案,再通过上述结论检测此答案是否可行,最后得到最小值。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;

int n,k,a[MAX],ch[MAX];

bool check(int x)
{
    int tot=k;
    for(int i=1; i<n; i++)
    {
        if(ch[i]<=x)continue;
        for(int j=1;; j++)
        {
            int cur=(ch[i]+j)/(j+1);
            if(cur<=x)
            {
                if(j<=tot)tot-=j;
                else return 0;
                break;
            }
        }
    }
    return 1;
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int L=1,R=-1,res;
        scanf("%d%d",&n,&k);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(int i=1; i<n; i++)
            ch[i]=a[i]-a[i-1],R=max(R,ch[i]);
        while(L<=R)
        {
            int mid=(L+R)>>1;
            if(check(mid))
            {
                res=mid;
                R=mid-1;
            }
            else
                L=mid+1;
        }
        printf("Case #%d: %d\n",++cas,res);
    }
}

Bundling

题意

n个字符串,要求分成大小为k的若干组(nk的倍数),每组的得分为该组所有字符串的最长公共前缀长度。求如何使得得分最大化,求出最大得分。

思路

显然公共前缀越长越好,所以贪心,找公共前缀最长的能分成一组的就分成一组,建一棵字典树,记录以某前缀开头的字符串数量。然后在字典树上贪心寻找尽可能深的,足够分成一组的前缀(即寻找以此前缀开头的字符串数量大于等于k的前缀),然后用深度更新答案,再返回已经用过的数量给父亲,更新父亲的数量。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e6+6;
typedef long long ll;

int nxt[MAX][26],sum[MAX],cnt,n,k;
ll res;
char ss[MAX];

void ins(char *s)
{
    int p=0,len=strlen(s);
    sum[p]++;
    for (int i=0; i<len; i++)
    {
        int c=s[i]-'A';
        if(!nxt[p][c])nxt[p][c]=++cnt;
        p=nxt[p][c];
        sum[p]++;
    }
}
int dfs(int x,int d)
{
    int ssum=0,cur=0;
    for(int i=0;i<26;i++)
        if(nxt[x][i])
            ssum+=dfs(nxt[x][i],d+1);
    sum[x]-=ssum;
    if(sum[x]>=k)
    {
        cur=sum[x]/k;
        res+=d*cur;
        sum[x]%=k;
    }
    return ssum+cur*k;
}
void init()
{
    memset(nxt,0,sizeof nxt);
    memset(sum,0,sizeof sum);
    cnt=0;
    res=0;
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",ss);
            ins(ss);
        }
        dfs(0,0);
        printf("Case #%d: %lld\n",++cas,res);
    }
}

猜你喜欢

转载自www.cnblogs.com/cryingrain/p/12569372.html
今日推荐