Programmation de la séance de réflexion semaine 3

Programmation de la séance de réflexion semaine 3

Le contenu principal de la pratique cette semaine est l'algorithme gourmand, principalement pour trouver le critère gourmand. Dans l'ensemble, les deux premières questions ont été très fluides. Chaque question a duré environ 20 minutes et la troisième a consacré plus de temps à réduire la complexité.

Problème A

Étant donné N nombres positifs, vous devez sélectionner K d'entre eux qui correspondent à S. Maintenant, veuillez calculer le nombre de façons de l'obtenir!

1. échantillon d'entrée et de sortie

Contribution

La première ligne, un entier T <= 100, indique le nombre de cas de test. Pour chaque cas, il y a deux lignes. La première ligne, trois entiers indiquent n, K et S. La deuxième ligne n entiers indique les nombres positifs.

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

Production

Pour chaque cas, un entier indique la réponse sur une ligne indépendante.

4

2. L'idée générale et le code

Parcourez le tableau d'entrée, avec deux options pour chaque élément: sélectionnez et désélectionnez. S'il est sélectionné, soustrayez l'élément de la différence avec la somme cible, puis examinez l'élément suivant. S'il n'est pas sélectionné, passez directement à l'élément suivant. Parmi eux, la condition de résiliation en temps opportun (élagage de faisabilité) est que la différence par rapport à la somme cible est inférieure à 0, ou que le nombre de nombres sélectionnés dépasse K mais que la cible et S ne sont pas encore atteints.

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

int tmp;
int n,m,K,S;
int *p;
void SOL(int i,int size,int sum)
{
    //符合K个数相加等于S的要求
    if(sum==0&&size==K)
    {
        tmp++;
        return ;
    }
    //提前结束情况
    if(sum<0||size>K||i>=m)
        return ;
    //如果选第i个数
    SOL(i+1,size+1,sum-p[i]);
    //不选
    SOL(i+1,size,sum);
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        tmp=0;
        //一共m个数,K个数的和为S的方案个数
        cin>>m;
        cin>>K;
        cin>>S;
        p=new int[m];
        for(int j=0;j<m;j++)
        {
            cin>>p[j];
        }
        SOL(0,0,S);
        cout<<tmp<<endl;
    }
}

Problème B Problème de sélection de point d'intervalle

Il y a n intervalles fermés [a_i, b_i] sur la droite numérique. Prenez le moins de points possible pour qu'il y ait au moins un point dans chaque intervalle (les points contenus dans différents intervalles peuvent être identiques)

1. échantillon d'entrée et de sortie

Contribution

1 entier N dans la première ligne (N <= 100)
Ligne 2 ~ N + 1, deux entiers a, b dans chaque ligne (a, b <= 100)

2
1 5
4 6

Production

Un entier représentant le nombre de points sélectionnés

1

2. L'idée générale et le code

Problème gourmand classique. Triez d'abord l'intervalle d'entrée du point final droit de petit à grand et utilisez lim pour enregistrer la valeur du point final droit de la section actuellement sélectionnée. Parcourez ensuite le tableau d'intervalles, si le point lim est dans l'intervalle en cours, passez à l'intervalle suivant. Sinon, affectez l'extrémité droite de l'intervalle à lim et sélectionnez le numéro d'intervalle ++.

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class section
{
public:
    section()
    {
        _a=0;
        _b=0;
    }
    section(int a,int b)
    {
        _a=a;
        _b=b;
    }
    inline bool operator < ( section & x)
    {
        return _b<x._b;
    }
public:
    int _a,_b;
};

bool cmp(section a,section b)
{
    return a._b<b._b;
}

int n;
vector<section> p;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int a,b;
        cin>>a;
        cin>>b;
        section Nsection(a,b);
        p.push_back(Nsection);
    }
    sort(p.begin(),p.begin()+n,cmp);
    int lim=p.front()._b;
    int tmp=1;
    for(int i=1;i<n;i++)
    {
        if(lim>=p[i]._a&&lim<=p[i]._b)
            continue;
        else
        {
            tmp++;
            lim=p[i]._b;
        }
    }
    cout<<tmp<<endl;
    return 0;
}

Problème C Problème de couverture d'intervalle

Il y a n (1 <= n <= 25 000) intervalles fermés [ai, bi] sur la ligne numérique. Choisissez le moins d'intervalles possible pour couvrir un segment de ligne spécifié [1, t] (1 <= t <= 1 000 000).
Couvrir tout le point, c'est-à-dire que (1,2) + (3,4) peut couvrir (1,4).
Impossible de faire la sortie -1

1. échantillon d'entrée et de sortie

Contribution

La première ligne: la
deuxième ligne de N et T à la ligne N + 1: chaque ligne a un intervalle fermé.

3 10
1 7
3 6
6 10

Production

Le nombre d'intervalles sélectionnés ne peut pas être sorti -1

2

2. L'idée générale et le code

Pour le problème gourmand, allez d'abord à une carte mentale pour résoudre le problème.
Processus de soumission de la question C

En termes de conception d'idées, triez d'abord le tableau d'intervalles d'entrée en fonction du point final droit. Utilisez mlim pour enregistrer la valeur de point d'extrémité la plus à droite de l'intervalle actuellement sélectionné et pos pour enregistrer la position de l'intervalle actuellement sélectionné dans le tableau. Chaque fois que la fonction de comptage est appelée, le tableau est parcouru de pos au début, sélectionnez la plage commençant par milm et la valeur d'extrémité droite est la plus grande, attribuez la nouvelle valeur d'extrémité droite à Mlim et enregistrez pos. Jusqu'à ce qu'il atteigne la valeur cible d'extrémité droite t. Parmi eux, la situation qui ne peut pas être atteinte est qu'il y a un point vide non récupérable entre les intervalles (indiqué dans le code que Mlim ne se voit pas attribuer une nouvelle valeur), et que le point final droit maximum ne peut pas atteindre la valeur cible t (dans le code, le début est égal à Mlim ).
La complexité finale du code est O (nlogn)

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

class section
{
public:
    section()
    {
        _a=0;
        _b=0;
    }
    section(int a,int b)
    {
        _a=a;
        _b=b;
    }
public:
    int _a,_b;
};

int n;//区间个数
int t;//目标右端点值
int wa=-1;//错误输出
int tmp=1;//区间个数计数
vector<section> p;//区间数组

bool cmp(section a,section b)
{
    return a._b<b._b;
}

void count(int s,int start)
{
    //记录从start开始的区间能够到达的最右点
    int Mlim=0;
    //记录下一次数组遍历的起始位置
    int pos=0;
    //判断是否成功结束
    if(start==t)
    {
        cout<<tmp;
        return;
    }
    //简化区间到【start,t】之中
    for(int i=s+1;i<p.size();i++)
    {
        if(p[i]._a<=start+1)
        {
            //p[i]._a = start;
            if(p[i]._b>Mlim)
            {
                Mlim = p[i]._b;
                pos=i;
            }
        }
    }
    if(Mlim==0||Mlim==start)
    {
        cout<<wa;
        return ;
    }
    else {
        tmp++;
        //cout<<Mlim<<endl;
        count(pos,Mlim);
    }
}

int main()
{
    while(cin>>n)
    {
        scanf("%d", &t);
        //记录从1起始的区间能够到达的右极点
        tmp=1;
        int mlim = 0;
        for (int i = 0; i < n; i++) {
            int a, b;
            scanf("%d", &a);
            scanf("%d", &b);
            if (b >= t) {
                b = t;
            }
            if (a <= 1) {
                a = 1;
                if (b > mlim)
                    mlim = b;
            }
            section Nsection(a, b);
            p.push_back(Nsection);
        }
        if (mlim == 0) {
            cout << wa;
            return 0;
        }
        //按右端点排序
        sort(p.begin(), p.begin() + p.size(), cmp);
        count(0, mlim);
        p.clear();
    }
    return 0;
}
A publié 8 articles originaux · Likes2 · Visites 252

Je suppose que tu aimes

Origine blog.csdn.net/lawrenceY/article/details/104761358
conseillé
Classement