Condivisione della tecnologia

"Note sull'algoritmo" Riepilogo n. 6 - Greedy

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Avidità semplice

        metodo golosoÈ una sorta di soluzioneProblema di ottimizzazione Questo metodo considera sempre la strategia per ottenere il risultato globale ottimale (o migliore) dopo l'ottimo locale (o migliore) nello stato attuale. Ovviamente, se si adotta una strategia migliore ma non ottimale (la strategia ottimale può non esistere o non è facile pensarla), il risultato globale ottenuto non potrà essere ottimale.Per ottenere il risultato ottimale, è necessario che ogni strategia di passaggio intermedio sia ottimale, quindi il metodo goloso viene utilizzato rigorosamente per risolvere il problema.ottimizzare La questione richiede la giustificazione della strategia adottata. L'idea generale della dimostrazione è quella di utilizzare la dimostrazione per contraddizione e induzione matematica, cioè assumendo che la strategia non possa portare alla soluzione ottima, e quindi ottenere la contraddizione attraverso una serie di derivazioni per dimostrare che la strategia è ottimale , e infine utilizzare l'induzione matematica per garantire l'ottimo globale. Tuttavia, per l'uso quotidiano, potrebbe non essere il momento o la facilità per dimostrare rigorosamente la strategia che ti viene in mente (la prova dell'avidità è spesso più difficile dell'avidità stessa), quindi in generale, se pensi a una strategia che sembra fattibile, E se non puoi fornire un controesempio, sii abbastanza coraggioso da metterlo in pratica.

1. Raggruppa il numero più piccolo

Dato un numero di numeri da 0 a 9, puoi disporre questi numeri in qualsiasi ordine, ma devi usarli tutti e rendere il numero di destinazione il più piccolo possibile (ovviamente, 0 non può essere il primo Ad esempio, inserisci). due 0, due 1, tre 5 e un 8, il numero più piccolo ottenuto è 100155858.


Credo che tu possa vedere subito la strategia: seleziona prima il numero più piccolo che non è 0 da 1 a 9 per l'output, quindi emetti i numeri da 0 a 9. Il numero di volte in cui ciascun numero viene visualizzato è il suo numero rimanente.

Prova della strategia corretta

  • Innanzitutto, poiché tutti i numeri devono partecipare alla combinazione, viene determinato il numero di cifre del risultato finale.
  • Poiché il bit più alto non è 0, scegli un numero il più piccolo possibile come primo bit: il teorema del bit più alto
  • Anche le restanti cifre dovrebbero essere emesse da piccole a grandi~

Quello nel libro di testo è davvero troppo astratto e sembra un po' sbagliato. Qui lo ha scritto lui stesso:

  1. #include <cstdio>
  2. #include <vector>
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6. int main() {
  7. vector<int> V;
  8. for(int i=1;i<=10;i++)
  9. {
  10. int temp=0;
  11. cin>>temp;
  12. V.push_back(temp);
  13. }
  14. sort(V.begin(),V.end()); //直接排成升序
  15. int flag=0; //标记
  16. for(int i=0;i<=9;i++)
  17. if(V[i]!=0)
  18. {
  19. int temp=V[i];
  20. V[i]=V[0];
  21. V[0]=temp;
  22. flag=i;//保存第一个不为0的位置
  23. break;
  24. }
  25. for(int i=flag+1;i<=9;i++) //找更小的头,直接从flag下一位开始即可,节省时间~
  26. if(V[i]<V[0]&&V[i]!=0)
  27. {
  28. int temp=V[i];
  29. V[i]=V[0];
  30. V[0]=temp;
  31. }
  32. for(int i=0;i<=9;i++)
  33. cout<<V[i];
  34. }

Logicamente non è difficile, l’importante è pensare chiaramente~

2. Inventario della torta lunare

  • Input: inserisci N e M nella prima riga: il numero di tipi di mooncake a N cifre e la domanda di mercato a M cifre per i mooncake devono essere inseriti nelle due righe successive: i numeri N nella prima riga corrispondono a i tipi attuali Quanto puoi guadagnare dopo aver venduto tutti i mooncakes, e il numero N nella seconda riga corrisponde al numero totale attuale di mooncakes~
  • Produzione richiesta: reddito massimo sotto una domanda specifica

Immagina se tu fossi il capo, come saresti "avido"? Ovviamente, se avessi solo bisogno di vendere quanti più mooncakes al prezzo unitario più costoso possibile entro una domanda limitata, non sarebbe possibile ottenere il maggior fatturato? Quella che segue è un'implementazione scritta dallo stesso blogger, che è diversa da quella del libro di testo:

  1. #include <cstdio>
  2. #include <vector>
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6. struct mooncake{
  7. double num; //总数
  8. double income; //总收入
  9. double price; //单价,需要自己计算
  10. };
  11. int main() {
  12. int N,M;
  13. cin>>N>>M;
  14. vector<mooncake> V;
  15. for(int i=1;i<=N;i++)
  16. {
  17. mooncake temp;
  18. V.push_back(temp);
  19. }
  20. cout<<"请输入数量:"<<endl;
  21. for(int i=1;i<=N;i++)
  22. {
  23. double num=0;
  24. cin>>num;
  25. V[i-1].num=num;
  26. }
  27. cout<<"请输入总价:"<<endl;
  28. for(int i=1;i<=N;i++)
  29. {
  30. double income=0;
  31. cin>>income;
  32. V[i-1].income=income;
  33. }
  34. for(int i=0;i<=N-1;i++)
  35. V[i].price=V[i].income/V[i].num; //计算单价
  36. //按单价降序排列!保证贵的尽可能多卖
  37. for(int i=0;i<=V.size()-1;i++)
  38. {
  39. for(int j=i;j<=V.size()-1;j++)
  40. if(V[j].price>V[i].price)
  41. {
  42. mooncake temp;
  43. temp=V[j];
  44. V[j]=V[i];
  45. V[i]=temp;
  46. }
  47. }
  48. for(int i=0;i<=V.size()-1;i++)
  49. cout<<"单价第"<<(i+1)<<"高的值为:"<<V[i].income<<" "<<V[i].price<<" "<<V[i].num<<endl;
  50. for(int i=0;i<=N-1;i++)
  51. cout<<V[i].num<<endl;
  52. int j=0; //使用的下标
  53. double count=0; //总利润
  54. while(M>0) //当还有需求量时
  55. {
  56. double doubt=0;
  57. doubt=M-V[j].num; //用M减去当前类型的额总量
  58. if(doubt>=0)//减了以后M还有剩余
  59. {
  60. M-=V[j].num;//当前种类全部卖出
  61. count+=V[j].income;//直接总价相加
  62. j++;
  63. cout<<V[j].num;
  64. }
  65. else if(doubt<0) //不够减这么多
  66. {
  67. count+=V[j].price*M;//剩余部分按照单价计算
  68. break;
  69. }
  70. }
  71. cout<<"最高利润值为:"<<count<<endl;
  72. return 0;
  73. }

Assaggia attentamente il ciclo whlie sopra: quando M non è 0, cioè c'è ancora domanda, vendi i mooncakes più costosi. Vendere uno per uno in ordine: se la domanda attuale è sufficiente per esaurire tutti i tipi attuali, il prezzo totale verrà aggiunto direttamente, se non è sufficiente per esaurire tutti quelli attuali, venderne quanti ce ne sono; sono e calcolano in base al prezzo unitario~

Proviamolo con il test case del libro di testo:

  • 3 20
  • 18 15 10
  • 75 72 45

Il risultato è 94,50, che è coerente con la risposta standard~

Inoltre, il blogger qui scrive direttamente l'ordinamento nella funzione principale, lo scrive in una funzione indipendente e poi lo chiama Sembra che ci sia un bug nel vettore del tipo di struttura e l'ordinamento non ha esito positivo motivo, puoi scriverlo nell'area commenti~

2. Intervallo goloso

La radice della domanda è la seguente:

Per questo tipo di domande, devi solo ricordare——Dai la priorità all'intervallo con il punto finale sinistro più grande

 

Parliamo del perché lo facciamo, come mostrato nella foto sopra: non è difficile da trovare,Per garantire quante più scelte possibili, quando un intervallo più lungo contiene un intervallo più breve, dobbiamo selezionare prima l'intervallo più breve, questo è facile da capire.

Per la situazione di cui sopra, come intervalli sovrapposti come 1 e 2, non è difficile scoprire che se si seleziona l'intervallo 1 con il punto finale sinistro più grande, occuperà solo la posizione 9, mentre se si seleziona l'intervallo 2, occuperà posizione 8. Posizione - Questo ovviamente non è in linea con l'idea di spendere meno soldi possibile (spendere meno spazio), quindi dovresti scegliere il più a sinistra possibile, in modo che ci siano più spazi vuoti a destra ~ come sopra,Possiamo vedere facendo calcoli a mano che ce ne sono al massimo 4 disgiunti.

Codice dal libro di testo:

  1. #include <cstdio>
  2. #include <algorithm>
  3. using namespace std;
  4. const int maxn=110;
  5. struct Inteval{
  6. int x,y; //开区间左右端点
  7. }I[maxn];
  8. bool cmp(Inteval a,Inteval b)
  9. {
  10. if(a.x!=b.x)
  11. return a.x>b.x; //左端点从大到小排序
  12. else
  13. return a.y<b.y; //左端点相同的按右端点从小到大排序
  14. }
  15. int main() {
  16. int n;
  17. while(scanf("%d",&n,n!=0))
  18. {
  19. for(int i=0;i<n;i++)
  20. scanf("%d%d",&I[i].x,&I[i].y);
  21. sort(I,I+n,cmp); //排序区间
  22. int ans=1,lastX=I[0].x;
  23. //ans记录总数,lastX记录上一个被选择的区间的左端点
  24. for(int i=1;i<n;i++)
  25. {
  26. if(I[i].y<=lastX) //如果该区间右端点在lastX左边
  27. {
  28. lastX=I[i].x; //以I[i]作为新选中的区间
  29. ans++; //不相交的区间个数+1
  30. }
  31. }
  32. printf("%dn",ans);
  33. }
  34. return 0;
  35. }

Tuttavia, ai blogger non piacciono ancora gli array originali. Ecco una versione con struttura vettoriale:

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. using namespace std;
  5. struct section{
  6. int x=0;
  7. int y=0;
  8. //x和y分别为左右端点
  9. };
  10. int main() {
  11. int n=0;
  12. vector<section> V;
  13. cin>>n;
  14. for(int i=1;i<=n;i++) //读入数据
  15. {
  16. section temp;
  17. int x=0,y=0;
  18. cin>>x>>y;
  19. if(x>y) //防止左端点大于右端点
  20. {
  21. int temp1=x;
  22. x=y;
  23. y=temp1;
  24. }
  25. else if(x==y) //若左右端点相同
  26. {
  27. i-=1; //则当前输入 不算
  28. cout<<"不可以输入相同的左右端点!"<<endl;
  29. continue; //舍弃数据,本次循环作废~
  30. }
  31. temp.x=x;
  32. temp.y=y;
  33. V.push_back(temp);
  34. }
  35. //按要求排序区间优先级
  36. for(int i=0;i<=V.size()-1;i++)
  37. {
  38. for(int j=i+1;j<=V.size()-1;j++)
  39. {
  40. if(V[j].x>V[i].x) //左端点越大越靠前
  41. {
  42. section temp=V[j];
  43. V[j]=V[i];
  44. V[i]=temp;
  45. }
  46. else if(V[j].x==V[i].x&&V[j].y<V[i].y) //左端点相同的话,右端点小的优先
  47. {
  48. section temp=V[j];
  49. V[j]=V[i];
  50. V[i]=temp;
  51. }
  52. }
  53. }
  54. cout<<"顺序如下:"<<endl;
  55. for(int i=0;i<=V.size()-1;i++)
  56. cout<<V[i].x<<"~"<<V[i].y<<endl;
  57. int count=1,lastX=V[0].x;
  58. //count用来统计总数,lastX是上一个符合条件的区间的左端点
  59. for(int i=1;i<=V.size()-1;i++)//直接从第二个区间开始
  60. {
  61. if(V[i].y<lastX) //如果当前区间的右端点不和上一个左端点相加,满足题意
  62. {
  63. lastX=V[i].x;
  64. count++;
  65. }
  66. }
  67. cout<<count<<endl;
  68. return 0;
  69. }

La prova è la seguente:

 


In generale, il metodo greedy è un'idea algoritmica utilizzata per risolvere un tipo di problema di ottimizzazione e spera di derivare il risultato ottimale globale da una strategia ottimale locale.algoritmo avido I problemi applicabili devono soddisfare le proprietà della sottostruttura ottimale, cioè la soluzione ottima di un problema può essere effettivamente costruita dalle soluzioni ottime dei suoi sottoproblemi. Ovviamente non tutti i problemi sono adatti al metodo greedy, ma questo non impedisce all'algoritmo greedy di diventare un algoritmo semplice, pratico ed efficiente~