牛客 假日团队赛32 (B (二分) E (栈模拟) F (康托展开) J(字符串dp) L (01 背包+方案数))

题目链接

B-Brownie Slicing

题意:给你一个n*m 的带权矩阵,现在给你  r c  要求每行分成 r 份,每份单独列切 分成 c 份,每份的美味值等于带权矩阵中的矩阵和,现在问你如何切 使得最小的 那份蛋糕 权值最大。

做法:二分 最小蛋糕 权值最大即可,然后check一下能否切r 行 c列份,区间和就是二维前缀和就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e2+10;
ll a[N][N],sum[N][N];
int n,m,x,y;
ll run(int l1,int r1,int l2,int r2)
{
    return sum[l2][r2]-sum[l2][r1-1]-sum[l1-1][r2]+sum[l1-1][r1-1];
}
int cal(ll mid)
{
    int l1,r1,l2,r2;
    l1=0,r1=1,l2=0,r2=1;
    int t=0;
    while(l2<=n){
        int num=0;//每一行
        l1=l2+1,r1=r2=1,l2=l1;

        while(l1<=n&&l2<=n){
            num=0;
            for(int r2=1;r2<=m&&r1<=m;++r2){
                if(run(l1,r1,l2,r2)>=mid) {
                    num++,r1=r2+1;
                }
            }
            if(num>=y) {
                t++;
                break;
            }
            else {
                l2++;
                r1=r2=1;
            }
        }

    }
    return t>=x;
}


int main()
{
    cin>>n>>m>>x>>y;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            scanf("%lld",&a[i][j]);
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        }
    }

    ll l=0,r=1e9,ans=0;
    while(l<=r){
        ll mid=l+r>>1;
        if(cal(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%lld\n",ans);
    return 0;
}

E-Best Parenthesis

题意:给你一行 括号已经匹配的字符串,

一个 ()  权值为1,有一个嵌套的就乘上2 例:(()) =2   ((())(()))=8 

做法:普通栈模拟下就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=12345678910;
const int N=1e5+10;
int n;
stack<ll>sta;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        ll x;
        scanf("%lld",&x);
        if(x==0) x=-1;
        else x=-2;

        if(x==-1) sta.push(-1);
        else {
            if(sta.top()==-1) sta.pop(),sta.push(1);
            else{
                ll d=0;
                while(sta.top()!=-1){
                    d=d+sta.top();sta.pop();
                    d%=mod;
                }

                sta.pop();

                sta.push(d*2%mod);
            }
        }
    }
    ll ans=0;
    while(sta.size()) {
        ans+=sta.top(),sta.pop();
        ans%=mod;
    }
    printf("%lld\n",ans);
}

F-Cow Line

题意:现在,已知N头牛的排列方式,求这种排列方式的行号。 
或者已知行号,求牛的排列方式。 
所谓行号,是指在N头牛所有可能排列方式,按字典顺序从大到小排列后,某一特定排列方式所在行的编号。 
如果,行号是3,则排列方式为1 2 4 3 5 
如果,排列方式是 1 2 5 3 4 则行号为5 

有K次问答,第i次问答的类型,由C_i来指明,C_i要么是‘P’要么是‘Q’。 
当C_i为P时,将提供行号,让你答牛的排列方式。当C_i为Q时,将告诉你牛的排列方式,让你答行号。 

做法:这是一个康托展开和康托逆展开的裸题,这个很简单

学习博客

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=22;
ll f[N],a[N],b[N];
int n,q,vis[N];
ll cal1()
{
    ll res=0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;++i){
        int num=0;
        for(int j=a[i]-1;j>=1;--j){
            if(vis[j]==0) num++;
         }
         //printf("num:%d f:%lld\n",num,f[n-i]);
         res=res+f[n-i]*num;
         vis[a[i]]=1;
    }
    return res+1;
}


void cal2(ll x)
{
    memset(vis,0,sizeof(vis));
    int l=n-1;
    int len=1;
    while(len<=n){
        int t=x/f[l],i=1;
        x=x%f[l];
        for(;i<=n;++i){
            if(vis[i]==0) {
                if(t) --t;
                else break;
            }
        }
        vis[i]=1;
        b[len]=i;
        ++len,--l;
    }
    for(int i=1;i<=n;++i) printf("%lld ",b[i]);
    puts("");
}
int main()
{
    f[0]=1;
    for(int i=1;i<N;++i) f[i]=f[i-1]*i;
    scanf("%d%d",&n,&q);
    while(q--){
        char s;
        cin>>s;
        if(s=='P'){
            ll x;
            cin>>x;
            cal2(x-1);
        }
        else{
            for(int i=1;i<=n;++i){
                cin>>a[i];
            }
            printf("%lld\n",cal1());
        }
    }
}

J-Cowlphabet

题意:输入u,l,p p行2长度的字符串  要你构造一个字符串 含有u个大写,l个小写  且任意相邻的字符串都是p中的一种。

做法:dp即可,dp[i][j][k]  代表构造的前i个字符含有j个小写,且末尾字符是k的方案数   大写数==i-j  省略了一维,然后稍微搞搞就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=260;
const ll mod=97654321;
ll dp[2*N][N][53];
int u,l,n;
string s[N];
int get(char x)
{
    if('a'<=x&&x<='z') return x-'a'+1;
    return x-'A'+27;
}
int check(int x)
{
    if(1<=x&&x<=26) return 1;
    return 0;
}
int main()
{
    cin>>u>>l>>n;
    for(int i=1;i<=n;++i){
        cin>>s[i];
        int t0=get(s[i][0]),t1=get(s[i][1]);
        int x=0;
        if(check(t0)) x++;
        if(check(t1)) x++;
        dp[2][x][t1]++;
    }

    for(int i=3;i<=u+l;++i){
        for(int j=0;j<=l;++j){
            for(int k=1;k<=n;++k){
                int t0=get(s[k][0]),t1=get(s[k][1]);
                if(dp[i-1][j][t0]==0) continue;

                int x=0;
                if(check(t1)) x++;
                dp[i][j+x][t1]+=dp[i-1][j][t0];
                dp[i][j+x][t1]%=mod;
            }
        }
    }

    ll ans=0;
    for(int i=1;i<=52;++i) ans+=dp[u+l][l][i],ans%=mod;
    printf("%lld\n",ans);

}

L-Dividing the Gold

题解来自洛谷

题意翻译
Bessie 和 Canmuu 发现了一个有 N 个金币的袋子(1 <= N <= 250) 。第i个金币有一个价值 Vi (1 <= Vi <= 2000)他们希望尽可能的把这堆金币平均分配,但有时这是不可能的。找出使两堆价值差最小的分配方案。如果不能完全平均分开,Bessie会获得较高价值那一堆。

方案数就是01 背包中把最初始的状态dp[0]=1  然后再跑一遍刚刚的转移方程就可以了

#include<bits/stdc++.h>
#define mod 1000000
using namespace std;
int n,a[350],tot,sum,f[500005];
long long dp[500005];
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),sum+=a[i];
    tot=sum;
    sum/=2;
    for(int i=1;i<=n;i++)
        for(int j=sum;j>=a[i];j--)
            f[j]=max(f[j],f[j-a[i]]+a[i]);
    printf("%d\n",tot-2*f[sum]);
    dp[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=tot;j>=a[i];j--)
            dp[j]=(dp[j]+dp[j-a[i]])%mod;
    printf("%lld",dp[f[sum]]);
    return 0;
}
发布了498 篇原创文章 · 获赞 66 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/104633365