プログラミング思考の第4週の宿題

第4週の割り当て

総括する

A:DDLへの恐怖

n個の割り当て。各割り当てには独自のDDLがあります。DDLの前に割り当てを完了できない場合は、対応するスコアを差し引く必要があります。入力割り当ての最小控除ポイントを見つけます。

1.サンプルの入力と出力

入力

入力にはT個のテストケースが含まれています。入力の最初の行は、テストケースの数である単一の整数Tです。

各テストケースは、ジョブの数を示す正の整数N(1 <= N <= 1000)で始まります。

次に2行。最初の行にはDDLを示すN整数が含まれ、次の行には控除ポイントを示す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

アウトアウト

テストケースごとに、最小の合計削減スコアをテストケースごとに1行出力する必要があります。

0
3
5

2.トピックのアイデアとコード

貪欲なアルゴリズムを実践するための質問。貪欲な基準は、最後の締め切りからトラバースし、最も多くのポイントが差し引かれた宿題を優先することです。ジョブのDDLと控除ポイントを入力した後、ジョブを並べ替えます。最初のキー値はDDLの日付で、2番目のキー値は控除ポイントです。並べ替えの条件は、小さいものから大きいものまでです。次に、最後のDDLから現在の日付としてトラバースし、まだ期限が切れていないジョブを選択し、現在の日付から最も多くのポイントを差し引いて、ジョブ配列のアイテムを削除します。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:4つのシリーズ

4つのシリーズA、B、C、D。各シリーズにはn個の番号があります。
各シーケンスから数値を取得する場合は、4つの数値の合計をゼロにするスキームの数を調べます。

1.サンプルの入力と出力

入力

最初の行:n(シーケンスの数を表す)(1≤n≤4000)

次のn行では、i番目の行に4つの数字があり、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

出力

異なる組み合わせの数が出力されます。

5

注意:

样例解释: (-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.サンプルの入力と出力

入力

複数の入力セット。毎回Nを入力します。これは、N個の数値があることを意味し、次に長さNのシーケンスcat、cat [i] <= 1e9、3 <= n <= 1e5を入力します。

4
1 3 2 4
3
1 10 2

出力

新しい配列ansの中央値を出力します

1
8

2.トピックのアイデアとコード

中央値を見つけるために回答二分法が使用され、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 as xi> = xj + Pを書き換えます。メイン関数でjをトラバースし、各jの条件を満たすiを検索します。検索関数では、xj + Pは固定値であり、最初の要素の添え字ansを見つけます>元の数値シーケンスでxx + Pを満たします。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件公開 Likes2 訪問数252

おすすめ

転載: blog.csdn.net/lawrenceY/article/details/104980387