Semana de atribuição do pensamento de programação3
O principal conteúdo prático desta semana é o algoritmo ganancioso, principalmente para encontrar o critério ganancioso. No geral, as duas primeiras perguntas foram muito suaves, cada pergunta durou cerca de 20 minutos e a terceira passou mais tempo reduzindo a complexidade.
Problema A
Dados N números positivos, você deve selecionar K deles que somam S. Agora calcule quantas maneiras de obtê-lo!
1. entrada e saída de amostra
Entrada
A primeira linha, um número inteiro T <= 100, indica o número de casos de teste. Para cada caso, há duas linhas. A primeira linha, três números inteiros, indica n, K e S. A segunda linha, números inteiros, indica os números positivos.
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Resultado
Para cada caso, um número inteiro indica a resposta em uma linha independente.
4
2. A idéia e o código gerais
Faça uma iteração na matriz de entrada, com duas opções para cada elemento: selecione e desmarque. Se selecionado, subtraia o elemento da diferença com a soma de destino e, em seguida, observe o próximo elemento. Se não selecionado, vá diretamente para o próximo elemento. Entre eles, a condição de finalização oportuna (remoção de viabilidade) é que a diferença da soma da meta é menor que 0 ou o número de números selecionados excede K, mas a meta e S ainda não foram atingidas.
#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;
}
}
Problema B Problema de seleção do ponto de intervalo
Existem n intervalos fechados [a_i, b_i] na linha numérica. Pegue o mínimo de pontos possível para que haja pelo menos um ponto em cada intervalo (os pontos contidos em diferentes intervalos podem ser os mesmos)
1. entrada e saída de amostra
Entrada
1 número inteiro N na primeira linha (N <= 100)
Linha 2 ~ N + 1, dois números inteiros a, b em cada linha (a, b <= 100)
2
1 5
4 6
Resultado
Um número inteiro representando o número de pontos selecionados
1
2. A idéia e o código gerais
Problema ganancioso clássico. Primeiro, classifique o intervalo de entrada do ponto final direito de pequeno a grande e use lim para registrar o valor do ponto final direito da seção atualmente selecionada. Em seguida, percorra a matriz de intervalos, se o ponto limite estiver no intervalo atual, continue no próximo intervalo. Caso contrário, atribua a extremidade direita do intervalo a lim e selecione o número do intervalo ++.
#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;
}
Problema C Problema na cobertura do intervalo
Existem n (1 <= n <= 25000) intervalos fechados [ai, bi] na linha numérica Escolha o menor número possível de intervalos para cobrir um segmento de linha especificado [1, t] (1 <= t <= 1.000.000).
Cubra todo o ponto, ou seja, (1,2) + (3,4) pode cobrir (1,4).
Impossível fazer saída -1
1. entrada e saída de amostra
Entrada
A primeira linha: a
segunda linha de N e T para a linha N + 1: cada linha tem um intervalo fechado.
3 10
1 7
3 6
6 10
Resultado
O número de intervalos selecionados não pode ser emitido -1
2
2. A idéia e o código gerais
Para o problema ganancioso, primeiro vá a um mapa mental para resolvê-lo.
Em termos de design de ideias, primeiro classifique a matriz do intervalo de entrada de acordo com o ponto final correto. Use mlim para registrar o valor do ponto final mais à direita do intervalo atualmente selecionado e pos para registrar a posição do intervalo atualmente selecionado na matriz. Cada vez que a função de contagem é chamada, a matriz será percorrida da posição até o início, selecione o intervalo que começa em milm e o valor final direito é o maior, atribua o novo valor final direito a Mlim e registre pos. Até atingir o valor-alvo correto do ponto final t. Entre eles, a situação que não pode ser alcançada é a de que existe um ponto vazio descoberto entre os intervalos (indicado no código que Mlim não recebeu um novo valor) e o ponto final máximo direito não pode atingir o valor-alvo t (no código, start é igual a Mlim )
A complexidade final do código é 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;
}