程序设计思维week4 作业题

WEEK 4 作业题

To sum up

A:DDL的恐惧

n 个作业,每个作业都有自己的 DDL,如在DDL之前完不成作业需要扣除相应的分数。求输入作业安排的最少减分。

1.Sample Input and Outout

Input

输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。

每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。

然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。

3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4

Outout

对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。

0
3
5

2.题目思路及代码

一道练习贪心算法的题目。贪心准则是,从最后一个截止日期向前遍历,优先做扣分最多的作业。在完成作业的DDL和扣分值输入后,对作业进行排序,排序第一关键值是DDL日期,第二关键值是扣分值,排序准则均按从小到大。然后从最后一个DDL作为当前date向前遍历,选取目前还未过期的且扣分最多的作业在当前date完成,并在作业数组中删除该项。如果碰到p.back().DDL<date,说明当前日期没有能做的作业,则继续向前遍历,直到遍历到第一天。最后数组中剩下的作业的扣分和即为最优解。
代码如下:

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int num;
int LostPoint;
class hwk
{
public:
    hwk()
    {
        DDL=0;
        de=0;
    }
    hwk(int a,int b)
    {
        DDL=a;
        de=b;
    }
public:
    int DDL;
    int de;//减分
};

vector<hwk> p;

bool cmp(hwk a,hwk b)
{
    if(a.DDL<b.DDL)
        return true;
    else if(a.DDL==b.DDL&&a.de<b.de)
        return true;
    else return false;
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        LostPoint=0;
        p.clear();
        cin>>num;
        for(int j=0;j<num;j++)
        {
            int ddl;
            cin>>ddl;
            p.push_back({ddl,0});
        }//输入作业DDL
        for(int j=0;j<num;j++)
        {
            int De;
            cin>>De;
            p[j].de=De;
        }//输入每个作业相应扣除的分数
        sort(p.begin(),p.begin()+num,cmp);
        //按照作业优先度排序
        for(int date=p.back().DDL;date>=1;date--)
        {
            //cout<<"date:"<<date<<endl;
            if(p.empty())
                break;
            if(p.back().DDL<date)
                continue;
            int max=p.size()-1;
            int k=p.size()-1;
            //挑选所有还未过期且具有最大减分值的作业
            while(p[k].DDL>=date&&k>=0) {
                //cout<<max<<' ';
                if (p[max].de < p[k].de)
                    max = k;
                k--;
            }
            //将这个作业做掉
            p.erase(p.begin()+max,p.begin()+max+1);
        }
        //计算最优失分值。
        while(!p.empty())
        {
            LostPoint=LostPoint+p.back().de;
            p.pop_back();
        }
        cout<<LostPoint<<endl;
    }
    return 0;
}

B:四个数列

四个数列 A,B,C,D,每个数列都有 n 个数字。
若 从每个数列中各取出一个数,求多少种方案使得 4 个数的和为 0。

1. Sample Input and Out

Input

第一行:n(代表数列中数字的个数) (1≤n≤4000)

接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字
(数字不超过 2 的 28 次方)

6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45

Output

输出不同组合的个数。

5

Note:

样例解释: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

2.题目思路及代码

是一道练习二分法的题目。由于n<=4000,需要用二分法来降低复杂度。方法是先计算数列A与数列B的和存储起来并排序。后在新数列中二分查找数列C与数列D元素和的相反数。可以将复杂度降到O(n^2+2logn)。在这其中要注意二分查找确定边界时等号的使用。

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;
int result=0;
int* A;
int* B;
int* C;
int* D;
vector<int> p1;

void count(int a)
{

    int tar1=0;//标记>=a的首个位置
    int tar2=0;//标记<=a的最后一个位置
    int st=0;//起始指针
    int end=p1.size()-1;//末尾指针
    int mid=(st+end)/2;//中间指针
    //cout<<'!'<<endl;
    while(st<=end)
    {
        //cout<<'!'<<endl;
        if(a<=p1[mid]) {
            tar1=mid;
            end = mid - 1;
            mid=(st+end)/2;
        }
        else {
            st = mid + 1;
            mid = (st + end) / 2;
        }
    }
    if(p1[tar1]!=a)
        return ;

    st=0;
    end=p1.size()-1;
    while(st<=end)
    {
        if(a>=p1[mid]) {
            tar2=mid;
            st = mid + 1;
            mid=(st+end)/2;
        }
        else {
            end = mid - 1;
            mid=(st+end)/2;
        }
    }
    result=result+(tar2-tar1+1);
}

int main()
{
    cin>>n;
    A=new int[n];
    B=new int[n];
    C=new int[n];
    D=new int[n];
    for(int i=0;i<n;i++)
    {
        cin>>A[i];
        cin>>B[i];
        cin>>C[i];
        cin>>D[i];
    }
    for(int i=0;i<n;i++) {
        for (int j = 0; j < n; j++)
            p1.push_back(A[i] + B[j]);
    }
    sort(p1.begin(),p1.begin()+p1.size());

    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            count(-(C[i]+D[j]));
    }
    cout<<result<<endl;
    return 0;
}

C TT的神秘礼物

给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。

1. Sample Input and Out

Input

多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5

4
1 3 2 4
3
1 10 2

Output

输出新数组 ans 的中位数

1
8

2.题目思路及代码

用到了答案二分法求中位数,并且在搜索新数组newList的时候也用到了二分法,一共用到了两次二分。
首先对输入数列进行排序,保证在xj-xi恒为正(j>i)以去掉绝对值。如果输入数列大小为n,则新数列大小N=n*(n-1)/2,如果N为奇数,N++,保证N/2一定为新数列的中位数。
然后进行二分答案,左边界为0,右边界为原数列的最大值减最小值,设中间数P=(l+r)/2。计算满足条件(xj-xi)<=P的二元组个数。如果小于N/2,左边界右移至P+1,反之,有边界左移至P。
现在只需要解决符合条件二元组的计数问题。将条件(xj-xi)<=P改写为xi>=xj+P。在主函数中遍历j,对于每一个j搜索符合条件的i。在find函数中,xj+P为固定值,在原数列中二分查找第一个满足条件>=xj+P的元素下标ans,j-ans 即为数列中对一个确定j符合条件的i的个数。对所有j进行求和,即可得到符合条件二元组个数。
代码如下:

#include<iostream>
#include<algorithm>
#include <cstdio>
using namespace std;
int n,N;
int P,tmp;
int* List;
int  l, r, ans,mid;


//返回newList中满足<=P的二元组(i,j)个数
int find(int p,int j)
{
    int L = 0, R = j;
    ans=j;
    while(L<R)
    {
        mid=(L+R)/2;
        if(List[mid]>=List[j]-P)
        {
            ans=mid;
            R=mid;
        } else{
            L=mid+1;
        }
    }
    return j-ans;
}

int main()
{
    while(cin>>n)
    {
        N=n*(n-1)/2;    //新数列的元素个数
        if(N%2==1) N++;
        List=new int[n];
        for(int i=0;i<n;i++)
            scanf("%d",&List[i]);
        sort(List,List+n);
        l = 0;
        r = List[n-1]-List[0];
        while (l < r)
        {
            //cout<<l<<' '<<r<<endl;
            P = (l+r) /2;
            tmp = 0;
            for (int j = 1; j < n; j++) //遍历j,寻找xj-xi<=P的个数
            {
                tmp = tmp + find(P, j);
                //cout<<tmp<<endl;
            }
            if (tmp < N/2)  //当计数小于n/2时。
                l=P+1;
            else    //当计数大于等于n/2时。
            {
                r=P;
            }
        }
        cout<<r<<endl;
    }
    return 0;
}
发布了8 篇原创文章 · 获赞 2 · 访问量 252

猜你喜欢

转载自blog.csdn.net/lawrenceY/article/details/104980387
今日推荐