牛客编程巅峰赛S1第4场 - 黄金&钻石(题解)

牛客编程巅峰赛S1第4场 - 黄金&钻石(题解)

A-牛牛分蛋糕

题意:

牛牛今天家里要来客人,所以牛牛今天特意做了他最拿手的两种蛋糕,但是他是一个有洁癖的人,所以他在分蛋糕时,有如下几个原则:
1.他不希望一个盘子里出现两种蛋糕
2.他希望每个盘子中都有蛋糕
3.他想让装有最少蛋糕数量的盘子中装有的蛋糕数量尽可能多。

输入:
5,2,3
输出:
1
说明:
只有一种方法把蛋糕分配到盘子里,即所有的盘子上都有一个蛋糕。

备注:

n,a,b(1 ≤ a, b ≤ 10^5, 2 ≤ n ≤ a + b)
第一个参数代表盘子的数量
第二个参数代表第一种蛋糕的数量
第三个参数代表第二种蛋糕的数量。
程序应返回:在所有分法中,蛋糕数量最少的盘子中分到最多的蛋糕数量。

思路:

  • 二分答案 x x x个最多蛋糕数量
  • 假设 x x x个最多蛋糕数量属于第一种蛋糕,设 i i i个盘子装了第一种蛋糕,则总数上满足 i ∗ x ≤ a & & ( n − i ) ∗ x ≤ b i*x \le a \&\& (n-i)*x \le b ixa&&(ni)xb
  • 假设 x x x个最多蛋糕数量属于第二种蛋糕,同上
  • 二分答案时check这个式子即可
  • 复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

Code:

class Solution {
public:
    /**
     * 处理函数,返回在所有分法中,蛋糕数量最少的盘子中分到最多的蛋糕数量
     * @param n int整型 n个盘子
     * @param a int整型 a蛋糕数量
     * @param b int整型 b蛋糕数量
     * @return int整型
     */
    bool check(int mid,int n,int a,int b){
        for(int i=1;i<=n;++i){
            if(i*mid*1ll<=a*1ll&&(n-i)*mid*1ll<=b*1ll){
                return 1;
            }
            if(i*mid*1ll<=b*1ll&&(n-i)*mid*1ll<=a*1ll){
                return 1;
            }
        }
        return 0;
    }

    int solve(int n, int a, int b) {
        // write code here
        int l=1;
        int r=a+b;
        int ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid,n,a,b)){
                ans=mid;
                l=mid+1;
            }
            else{
                r=mid-1;
            }
        }
        return ans;
    }
    
};

B-牛牛凑数字

题意:

牛牛今天逛商店,看到商店里摆着一些很漂亮的数字,牛牛非常喜欢,想买一些数字带回家。
数字一共有九种类型,分别是 1 − 9 1-9 19这九个数字,每个数字的价钱都不一样,而且每个数字的货源都非常充足。
牛牛是个完美主义者,他希望用自己的能够承受的价格,从这些数字里面购买,并且凑到最大的数字带回家。

输入:
2,[9,11,1,12,5,8,9,10,6]
输出:
"33"
说明:
购买2个第3个数字,可以凑到最大值为33。

备注:

第一个参数为一个整数n(0 ≤ n ≤ 106),代表牛牛所能承受的价格。
第二个参数为1-9这九个数字的价格数组,a1,a2,……,a9(1≤ ai ≤10^5)。
程序应返回:一个数字,代表牛牛能凑到的最大的数字。当然,如果牛牛一个数字都买不起,返回"-1"即可。
注意,由于数字可能会很大,所以程序中需要处理成string类型进行返回。

思路:

CF原题可还行,赛前前一段时间练过,CF349B-Color-the-Fence

  • 优先贪最多位数最大值,如果能选最长的有 2222 2222 2222 3333 3333 3333,很明显要 3333 3333 3333
  • 将处理出来的位数,从高位往底位用剩余的费用优先换最大的可换数,例如 2222 2222 2222,这时可以将第一位换成 3 3 3 4 4 4,明显换 4 4 4更优,因为受高位影响,如果剩余费用还有 2 2 2,换 3 3 3的费用是 1 1 1,换 4 4 4的费用是 2 2 2,前者结果为 3322 3322 3322,后者结果为 4222 4222 4222,还是后者优,所以贪高位往低位的最大可换数是最优解。

Code:

struct node
 {
 	int v;
 	int id;
 }q[20];

#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
class Solution {
public:
    /**
     * 得到牛牛能够凑到的最大的数字
     * @param n int整型 牛牛能够承受的价格
     * @param a int整型vector 1-9这九个数字的价格数组
     * @return string字符串
     */
    long long sum[2000005];
    string solve(int n, vector<int>& a) {
        // write code here
           
        map<long long,long long>mp;
        int v;
        v=n;
        string ss;
        if(v==0){
            ss="-1";
            return ss;
        }
        for(int i=1;i<=9;++i){
            int k;
            q[i].v=a[i-1];
            q[i].id=i;
            mp[i]=a[i-1];
        }
        int cnt=0;
        int num=0;
        int maxs=-1;
        for(int i=1;i<=9;++i){
            if(v/mp[i]>=cnt){
                if(maxs<=(v-cnt*mp[i])){
                    num=i;
                    cnt=v/mp[i];
                    maxs=(v-cnt*mp[i]);
                }
            }
        }
        //debug(num);
        string s;
        v-=cnt*mp[num];
        rep(i,1,cnt)s+=num+'0';
        rep(i,0,s.size()){
            dep(j,9,1){
                if(v-(mp[j]-mp[s[i]-'0'])>=0&&j>s[i]-'0'){
                    v-=(mp[j]-mp[s[i]-'0']);
                    s[i]=j+'0';
                    break;
                }
            }
        }
        if(s.size()==0){
            s="-1";
            return s;
        }
 
        return s;
    }
    
};

C-牛妹的野菜

题意:

书接上回,牛妹组织春游,有一个有趣的项目是挖番薯。聪明的牛妹拿到了一个表明了番薯洞的地图,每个番薯洞中有一定数量的番薯。同时,我们知道番薯洞的连接路径,并规定路径是单向且小序号指向大序号,也无环。可以从任意一处开始挖,然后沿着连接往下挖(仅能选择一条路径),当无连接时,结束。
设计一种挖番薯的方案,使得可以挖到更多的番薯。
输出路径。

输入:
[5,10,20,5,4,5],[[1,2],[1,4],[2,4],[3,4],[4,5],[4,6],[5,6]]
输出:
"3-4-5-6"
说明:
很明显,先去第三点拿20个番薯,再去第四个点拿5个,再去第五个点拿4个,再去第六个点拿5个。这个方案最优

备注:

总番薯数量不超过1000000,番薯洞数量不超过250.

思路:

裸的有向图求最长路路径

  • 建图,假设起点为 0 0 0,将 0 0 0与番薯洞 i i i相连,边权为 w [ i ] w[i] w[i](番薯洞的点权)
  • 番薯洞的连接路径 l → r l\to r lr的边权为 w [ r ] w[r] w[r]
  • 最后跑源点为 0 0 0的最长路,松弛时记录路径即可

吐槽:Dijstra不能求最长路,Floyd能被卡30%超时,最后写了个SPFA才过掉。

Code:

class Solution {
public:
    /**
     * 
     * @param potatoNum int整型vector 依次表示序号为1,2,3..的番薯洞各有多少个番薯
     * @param connectRoad int整型vector<vector<>> 每个一维数组[x,y]表示从第x号番薯洞到第y号有路
     * @return string字符串
     */
        int pa[400000];int w[40000];
        int n,m,s,t;
        int vis[40000];
        int d[40000];
        struct node{
            int to;
            int next;
            int dis;
        }edge[400000];
        int in[400000];
        int head[400000],u,v,_d,base;
        int num_edge=0;
        void edge_add(int from,int to,int dis){
            edge[++num_edge].next=head[from];
            edge[num_edge].to=to;
            edge[num_edge].dis=dis;
            head[from]=num_edge;
        }
        void init(int n){
            for(int i=0;i<=0;++i){
                for(int j=1;j<=n;++j){
                    if(i==0){
                        edge_add(i,j,w[j]);
                    }
                }
            }
        }
        string so(int x){
            string ss;
            while(x){
                ss+=x%10+'0';
                x/=10;
            }
            reverse(ss.begin(),ss.end());
            return ss;
        }
        void spfa(){
            //vis[i] 为1表示点i属于S集合 为0表示点i属于T集合
             memset(d,-0x3f3f3f3f,sizeof(d));
            d[0]=0;
            vis[0]=1;
            in[0]++;
            queue<int>q;
            q.push(0);
            while(!q.empty()){
                int x=q.front();
                q.pop();
                vis[x]=0;
                for(int i=head[x];i;i=edge[i].next){
                    int y=edge[i].to;
                    int z=edge[i].dis;
                    if(d[y]<d[x]+z){//最长路
                        in[y]++;
                        d[y]=d[x]+z;
                        pa[y]=x;//记录路径
                        if(vis[y]==0){
                            q.push(y);vis[y]=1;
                        }
                    }
                }
            }

        }
        string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
                // write code here
                // 
            int n=potatoNum.size();
            int cnt=0;
            for(int i=0;i<(int)potatoNum.size();++i){
                w[++cnt]=potatoNum[i];
            }
            init(potatoNum.size());
            for(int i=0;i<(int)connectRoad.size();++i){
                int l=connectRoad[i][0];
                int r=connectRoad[i][1];
                edge_add(l,r,w[r]);
            }
            spfa();
            int maxx=0;
            int st;
            int ed;
            for(int i=0;i<=0;++i){
                for(int j=0;j<=n;++j){
                    if(d[j]>maxx){
                        maxx=d[j];
                        st=i;
                        ed=j;
                    }
                }
            }
            string ans;
            std::vector<int>path;
            path.push_back(ed);
            while(st!=ed){
                if(pa[ed]==st){
                    break;
                }
                else{
                    path.push_back(pa[ed]);
                }
                ed=pa[ed];
            }
            reverse(path.begin(),path.end());
            for(int i=0;i<(int)path.size();++i){
                if(i!=(int)path.size()-1){
                    ans+=so(path[i]);
                    ans+='-';
                }
                else ans+=so(path[i]);
            }
            //cout<<ans;
            return ans;
        }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值