腾讯2018.4.5编程题

由于搞不到原题,所以就只能叙述一下题意了。


1. n个连续的整数(1,2,3,4...n),然后要每隔m个数进行一次翻转符号,最开始是负号。保证2m整除n,比如n=8,m=2的话,这个序列就是-1,-2, 3, 4, -5, -6, 7 ,8,然后求前n项和。

题解:规律很显然,就是3+(-1) = 2, 4+(-2) = 2, 然后就是m*n/2了,但是n和m都要用long long不然会数据溢出的。


2. 长度为A的东西X个,长度为B的东西Y个,然后要组成恰好长度K的东西,不用考虑内部顺序,求一共有多少种组合方式。答案对1e9+7取模。

0<=X,Y<=100,0<K<=1000。好像是这样的数据范围。


题解:也是一道比较简单的排列组合计数题,只需要枚举A或者B,然后检测另外一个能不能恰好组成,然后就是C(X, A)+C(Y, B)了,全部加和,然后取模就是了。本来一开始以为要进行除法取模,后来一看组合数的范围只有100,那就利用组合数的加法公式进行n^2初始化就好了。C(m,n) = C(m-1,n)+C(m-1,n-1)。

注意A的总和超过K,以及B的数量是否够用这些边界情况就好了。

代码:

#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int mod = 1e9+7;
const int INF = 1<<30;

ll C[105][105];

void init( )
{
    memset(C, 0, sizeof(C));
    for (int i = 0; i <= 100; i ++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j ++)
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
    }
}

int main( )
{
    //freopen("input.txt", "r", stdin);
    int K, A, X, B, Y;
    scanf("%d%d%d%d%d", &K, &A, &X, &B, &Y);
    ll ans = 0;
    init( );

    for(int i=0; i<=X; i++)
    {
        int remain = K-i*A;
        if(remain<0 || remain%B!=0 || remain/B>Y)//can't zucheng
            continue;
        int num = remain/B;
        ll t = (C[X][i]*C[Y][num])%mod;
        ans = (ans+t)%mod;
    }
    cout<<ans<<endl;
    return 0;
}


3. 这个题和杭电OJ的一道题一模一样,就是HDU4864了,是一个比较好的贪心题目。

题解:

(1) 笔试的时候,想的太简单了。直接对两个struct数组进行双关键字排序(时间为首,等级为次,为了收益最大),然后用两个指针分别扫任务和机器,看起来很正确,但是是错的,只过了30%。比如这样的样例,有一个任务(200, 100),机器有(300, 50),(250, 150)和(100, 200)这样的情况。无论如何这个任务都会被滤掉,不会被执行,但是事实上,这个任务是可以被完成的。因为被另外两个机器的滤掉了。

(2)发现了问题就比较好解决了,只需要分别枚举两个关键字,然后将符合第一个关键字的任务或机器加入容器中,而不是直接滤掉,然后在对第二关键字进行贪心的匹配就好了,写法感觉很多种,总之就是贪心一定要贪对。

我这里的写法是,对任务枚举,按照时间和等级降序,这样保证取到的一定是收益最大的解(因为时间的收益严格大于等级,所以时间作为第一关键字,如果能完成收益大的任务,自然不会去完成收益低的任务);然后在此基础上,加所有工作时间大于等于任务的机器加入multiset中,取出等级最接近任务等级的,且大于等于任务等级的,就是lower_bound,这样可以保证完成的任务一定是最多的,不会遗漏。因为任务和机器都按照时间进行过排序,那么在set中机器的时间一定是可以完成后续任务的,它们是被时间更长的任务选择进来的,所以在此不需要考虑机器的工作时间了,在后面他们的时间不会有区别都是一样的,为了防止后面出现等级更大的任务,那么肯定要选择等级刚好大于任务的机器了),这样两次贪心下来得到的就是一定是最优解了,而且第二次贪心可以二分,不会超时。

(hdu里的系数是500和2,笔试题里是200和3)

代码:

#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
const int INF = 1<<30;

struct node//按照时间优先的顺序
{
    int x, y;
    bool operator<(const node& n) const
    {
        if(x !=  n.x)
            return x>n.x;
        return y>n.y;
    }
}works[maxn], mars[maxn];

struct fit//在集合中选择的时候,只选择等级最接近当前的任务的机器
{
    int x, y;
    bool operator<(const fit& f) const
    {
        return y<f.y;
    }
    fit(int xx = 0, int yy = 0)
    {
        x = xx;
        y = yy;
    }
};
multiset<fit> st;

int main( )
{
    //freopen("input.txt", "r", stdin);
    int n, m;
    while(~scanf("%d%d", &n, &m))
    {
        ll ans = 0, num = 0;
        for(int i=0; i<n; i++)
            scanf("%d%d", &mars[i].x, &mars[i].y);
        for(int i=0; i<m; i++)
            scanf("%d%d", &works[i].x, &works[i].y);
        sort(mars, mars+n);
        sort(works, works+m);

        st.clear( );
        int j = 0;
        //对任务进行贪心枚举
        for(int i=0; i<m; i++)
        {
            //第一关键字处理,优先选取时间,这样保证收益最大
            while(j<n && mars[j].x>=works[i].x)
            {
                //将时间足够完成任务的机器全部加入set中,进行选择
                st.insert(fit(mars[j].x, mars[j].y));
                j++;
            }

            //第二关键字处理
            //二分查找机器等级刚好大于任务等级的机器,这样保证能将完成的任务量最大化
            if(!st.empty())
            {
                multiset<fit>::iterator it = st.lower_bound(fit(0, works[i].y));
                if (it == st.end())
                    continue;
                num++;
                ans += 500*works[i].x + 2*works[i].y;
                st.erase(it);
            }
        }
        cout<<num<<" "<<ans<<endl;
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值