【ハッシュ、ダブルポインター】| AcWing1532。コインを探す

タイトル説明

エヴァは宇宙全体からコインを集めるのが好きです。

ある日、彼女は宇宙のショッピングモールに買い物に行き、チェックアウト時にさまざまなコインを使って支払うことができました。

ただし、特別な支払い要件があります。請求書ごとに、消費量を正確に支払うために使用できるコインは2枚だけです。

彼女が持っているすべてのコインの金種を考えると、彼女が与えられた金額を支払うために2つのコインを見つけることができるかどうかを彼女が判断するのを手伝ってください。

入力形式
最初の行には、コインの数と支払われる金額をそれぞれ表す2つの整数NとMが含まれています。

2行目には、各コインの金種を表すN個の整数が含まれています。

出力フォーマット
V1≤V2およびV1 + V2 = Mとなるように、選択した2つのコインの金種を表す2つの整数V1、V2を含む1行を出力します

答えが一意でない場合は、V1が最小の解が出力されます。

解決策がない場合は、「解決策なし」を出力します。

データ範囲
1≤N≤10 5
1≤M≤1000

入力例1:

8 15
1 2 8 7 2 4 11 15

出力サンプル1:

4 11

入力例2:

7 14
1 8 7 2 4 11 15

出力サンプル2:

No Solution

ハッシュ

この質問には欠陥があります。コインのサイズ範囲はわかりません。入力されたコインの数は1e5だけです。

  1. コインの範囲が1e5よりもはるかに大きい場合は、ハッシュを選択することをお勧めします。
  2. コインの範囲が1e5未満で、入力するコインの数が1e5である場合、コインが重複している必要があります。ハッシュする必要はありませんが、1e5配列を開いて数を記録するだけです。コインであり、値はコインの数を示します。

まず、コインのサイズが負でない数、つまり1以上であることを確認する必要があります。

アイデア
入力時にデータを処理します。データxを入力するたびに、数値xがm以上の場合、一致してはなりません。次の入力データを見つけることができます。そうでない場合は、以前のデータを探します。前に入力しましたが、見つけることができますか?a y、y=m-xmにすることができます。可能であれば保存された結果(v1、v2)のv1が(x、y)の小さい方よりも小さいかどうかを判断します。小さいほど、Knowing (v1、v2)よりも答えの選択に近くなります。

コインは2つしか選択されていないため、コインが存在する可能性があり、重複する要素を保存する必要はありません。

特殊な場合:mが偶数の場合、x = y = m / 2の場合がありますが、入力がx = m / 2の場合、最初の入力であるため、特別な判断は必要ありません。絶対に不可能です。、そして挿入します。2回目にx = m / 2を入力したときに、y = m / 2が以前に入力されたかどうかを最初に判断したときに見つかった場合は、mにすることができます。次に、現在のxが以前に記録されているかどうかを判断します。挿入前に記録がない場合、これは重複要素の存在を制限するためです。したがって、m / 2は1つだけですが、結果には影響しません。
通常、 xとyの両方が挿入されます。x + y = m

ただし、yが以前に記録されているかどうかx挿入する順序比較することに注意してください最初に比較してから、を挿入する必要あります
なぜなら、最初に挿入する場合は
、x = m / 2の場合、最初にxを挿入してから、yが挿入されているかどうかを照合します。この時点でy == xの場合、一致は新しく挿入されたxですが、実際にはxのみですが、順序の問題のため、一致は成功しました。

したがって、ハッシュテーブルに元々存在していた番号yと一致させ、xを挿入する必要があるかどうかを判断してから、xを挿入する必要があります。

1.手書きのハッシュ

#include <iostream>
#define read(x) scanf("%d",&x)

using namespace std;

const int N=3e5+1,M=1010; "N要是素数"
int hs[N];"初始化为0,当遇到0的时候说明哈希表这个位置为空,因为硬币的价值都是大于0的"
int null=0;  "所以哈希表中的0表示null"

int find(int x)
{
    
    
    int k=x%N;
    while (hs[k] && hs[k]!=x) {
    
     "如果遇到的数hs[k]不是0并且不是不是要找的x就不停,继续解决冲突"
        k++;                    "直到遇到0,说明表中不存在x,或者遇到x,说明找到了x"
        if (k==N) k=0;
    }
    return k;  "要么h[k]为空,表示x不存在,k是插入的位置;要么h[k]=x,k是匹配的位置"
}     其实,在这个题里,这个k就是x。

int main()
{
    
    
    int n,m;
    read(n),read(m);
    int v1=M,v2; //(v1,v2)v1先初始化一个可比较且不可能的数
    int x,y;
    while (n--) {
    
    
        read(x);
        if (x>=m) continue; //必然不成立
        y=m-x;  //此时y必然大于0
        int kx=find(x),ky=find(y); //找到x和y在哈希表中存储的下标,或者是否存在于哈希表中
        if (hs[ky]) {
    
     //如果y存在的话,因为输入的是x,所以x必然存在
            if (v1>min(x,y)) v1=min(x,y),v2=max(x,y);
        }
        //接下来判断x是否需要插入,当hs[kx]==null时,说明不存在x,需要插入
        if (!hs[kx]) hs[kx]=x;
    }   "且这个插入必须放在判断if(hs[ky])后面,避免当出现x+x=m的情况时出错。否则当第一次插入x(x=m/2)时就会匹配到,会出错。"
    v1==M?puts("No Solution"):printf("%d %d",v1,v2);

    return 0;
}

2.(1)の簡略化された記述

問題のデータは1e5コインに制限されています。Mを構成するためにそれらの2つを選択してください。Mの最大値は1000です。
入力コインの金種はわかりません。金種が1から1e5であると仮定して、1e5に開いたバブル配列を設定します。下付き文字は金種に対応し、対応する値は金種の数を示します。

#include <iostream>
#define read(x) scanf("%d",&x)

using namespace std;

const int N=1e5+10,M=1010;
int bubble[N];     "记录从1到N的面值的硬币的个数"

int main()
{
    
    
    int n,m;
    read(n),read(m);
    int v1=M,v2,x,y;
    while (n--) {
    
    
        read(x);
        y=m-x;
        if(y>0 && bubble[y]) //避免数组越界,y为负数时,即相当于x>=m时不处理
            if (v1>min(x,y)) v1=min(x,y),v2=max(x,y);
        bubble[x]++;    
    }
     v1==M?puts("No Solution"):printf("%d %d",v1,v2);
    
    return 0;
}

3.Unordered_setの実装

#include <iostream>
#include <unordered_set>
            
using namespace std;

#define read(x) scanf("%d",&x)
const int M=1010;

int main()
{
    
    
    int n,m;
    read(n),read(m);
    int v1=M,v2,x,y;
    unordered_set<int> hash;   //set中不允许有重复元素
    while (n--) {
    
     //边读入边处理
        read(x);
        y=m-x;
        if (hash.count(y)) {
    
    //x已经必然存在
            if (v1>min(x,y)) v1=min(x,y),v2=max(x,y);
        }
        hash.insert(x);  //重复则不插入
    }
    v1==M?puts("No Solution"):printf("%d %d",v1,v2);
    
    return 0;
}

ダブルポインタ

入力後、統合処理後、入力と処理を同時に行うことはなくなりました。

#include <iostream>
#include <algorithm>
#define read(x) scanf("%d",&x)

using namespace std;

const int N=1e5+10;

int main()
{
    
    
    int n,m;
    read(n),read(m);
    int coin[N];
    for (int i=0;i<n;i++) read(coin[i]);
    sort(coin,coin+n); //默认升序
    int i,j;
    for (i=0,j=n-1; i<j ; ) {
    
      //左边往右走,右边往左走,相碰就可以停,相碰说明只剩下选一个硬币的方案,即没答案
        if (coin[i]+coin[j]>m) j--;   "只能用if"
        else if (coin[i]+coin[j]<m) i++;
        else break; //碰到的第一个匹配到的就可以停止,coin[i]此时最小
    }
    i==j?puts("No Solution"):printf("%d %d",coin[i],coin[j]);  "必然不会出现i>j的情况,只可能i==j退出"
    
    return 0;
}

2つの処理の違いを比較する

	int i,j;
    for (i=0,j=n-1; i<j ; i++) {
    
      //左边往右走,右边往左走,相碰就可以停,相碰说明只剩下选一个硬币的方案,即没答案
        while (i<j && coin[i]+coin[j]>m) j--;
        if (i<j && coin[i]+coin[j]==m) break;
    }
    i>=j?puts("No Solution"):printf("%d %d",coin[i],coin[j]);   "可能出现i>j的情况"

おすすめ

転載: blog.csdn.net/HangHug_L/article/details/114143449