Diretório de artigos
A prova escrita online de uma determinada residência é legal, então enviei para ver se alguém pode me dar alguma dica de como resolver o problema;
1. Tópico 1
1.1 Pergunta
Existem duas matrizes com comprimento n (1 - 5.000), ou seja, a e b (1 - 1.000.0000). O grau de preferência é definido como a soma de ai*bi. Você pode escolher um intervalo de b e invertê-lo uma vez para
perguntar para o grau máximo de preferência
. Exemplo:
3
10 1 1
1 10 1
Saída:
102
Explicação:
O intervalo da matriz b invertida é [1, 2], após invertê-la é 10 1 1, uma matriz é 10 1 1, o os bits correspondentes são multiplicados e somados até 102;
1.2 Ideias de soluções violentas
Idéia:
enumerar os intervalos, para cada intervalo, inverter os elementos do intervalo na matriz, calcular o grau semelhante após a inversão e atualizar o valor máximo e, finalmente, gerar o grau semelhante máximo. A complexidade do tempo é o código O (n ^ 3)
:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
// 计算原始的喜欢程度
long long ans = 0;
for (int i = 0; i < n; i++) {
ans += (long long)a[i] * b[i];
}
// 枚举区间,计算翻转区间后的喜欢程度
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
// 翻转区间
reverse(a.begin() + i, a.begin() + j + 1);
// 计算喜欢程度
long long cur = 0;
for (int k = 0; k < n; k++) {
cur += (long long)a[k] * b[k];
}
// 更新最大值
ans = max(ans, cur);
// 恢复原数组
reverse(a.begin() + i, a.begin() + j + 1);
}
}
cout << ans << endl;
return 0;
}
1.3 Ideias de otimização para geração de IA
Idéia:
posso fornecer uma solução baseada em algoritmo ganancioso e otimização de soma de prefixos, com complexidade de tempo de O (n).
Primeiro, podemos converter o produto ai bi em (a+b) (ab), onde a = (ai+bi)/2 e b = (ai-bi)/2. Precisamos apenas considerar os maiores (a+b) e (ab).
Tomando (a+b) como exemplo, podemos primeiro calcular a diferença entre todos os dois elementos adjacentes diff[i] = (a[i+1]+b[i+1]) - (a[i]+b [ i]), então você pode julgar se a posição do intervalo precisa ser invertida com base no sinal da diferença. Especificamente, definimos sum1 = a[1]+b[1], sum2 = a[1]-b[1] e, em seguida, fazemos um loop da posição i=2:
- Se diff[i] > 0, significa que a[i]+b[i] é maior que a[i-1]+b[i-1], o que significa que há melhores escolhas para os intervalos candidatos subsequentes. Registramos maxSum = max(maxSum, sum1+sum2) e marcamos o intervalo [start, i] que precisa ser invertido e atualizamos sum1 e sum2 ao mesmo tempo.
- Se diff[i] <= 0, significa que a posição atual i-1 pode precisar ser adicionada ao intervalo candidato e só precisamos atualizar sum1 e sum2.
Para a atualização de maxSum, se o intervalo atual que precisa ser invertido for [início, fim], então podemos inverter a[início], b[início] e a[fim], b[fim], ou seja, trocar a[start] e a[end], troque b[start] e b[end] para obter o novo (maxSum, maxA, maxB). Observe que precisamos apenas selecionar o segmento com o maior a+b no intervalo que precisa ser invertido, mas não precisamos selecionar o maior intervalo que precisa ser invertido. Porque é possível que a diferença entre alguns intervalos seja muito pequena e seja invertida muitas vezes, resultando em alta complexidade de tempo.
Finalmente, precisamos calcular a existência do intervalo invertido [início, fim] novamente e realizar a otimização da soma do prefixo em a e b, respectivamente, para calcular rapidamente a soma do intervalo. Especificamente, calculamos os prefixos e sa e sb de a e b respectivamente, e então o grau máximo de preferência n é igual a a e sa no intervalo [1, start-1], mais a e sa no intervalo [start, fim] O valor absoluto da diferença, mais a e sa no intervalo [fim+1, n], é multiplicado por b e sb. Cálculos semelhantes podem obter a resposta.
O código é implementado da seguinte forma:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
int n, a[MAXN], b[MAXN];
long long sa[MAXN], sb[MAXN];
int diff[MAXN], pos[MAXN];
long long maxSum, maxA, maxB;
void solve()
{
sa[0] = sb[0] = pos[0] = 0;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i];
sa[i] = sa[i - 1] + a[i];
sb[i] = sb[i - 1] + b[i];
pos[i] = i - 1;
diff[i] = a[i] + b[i] - a[i-1] - b[i-1];
}
maxSum = sa[1] + sb[1] + sa[n] - sa[1] + sb[n] - sb[1];
maxA = sa[1] - sb[1];
maxB = sb[1] - sa[1];
int start = 1, end = 0;
long long sum1 = sa[1] + sb[1], sum2 = sa[1] - sb[1];
for (int i = 2; i <= n; ++i) {
if (diff[i] > 0) {
long long tmpSum1 = sum1 + sa[i] - sa[start] + sb[n] - sb[i-1];
long long tmpSum2 = sum2 + sa[i] - sa[start] - sb[n] + sb[i-1];
if (tmpSum1 + tmpSum2 > maxSum) {
maxSum = tmpSum1 + tmpSum2;
maxA = tmpSum1 / 2;
maxB = tmpSum2 / 2;
if (i <= n - 1 && diff[i+1] <= 0 && sa[i+1]-sa[start] > sb[n]-sb[i]) {
++i;
tmpSum1 += sa[i] - sa[start] - sb[n] + sb[i-1];
tmpSum2 += sb[n] - sb[i-1] - sa[i] + sa[start];
}
start = pos[i] + 2;
end = i;
}
sum1 += sa[i] - sa[start] + sb[i] - sb[start-1];
sum2 += sa[i] - sa[start] - sb[i] + sb[start-1];
} else {
sum1 += sa[i] - sa[i-1];
sum2 += sb[i] - sb[i-1];
pos[i] = pos[i-1];
}
}
sum1 = sa[n] - sa[start-1];
sum2 = sb[n] - sb[start-1];
for (int i = n; i >= end+1; --i) {
diff[i] = a[i] + b[i] - a[i-1] - b[i-1];
if (diff[i] <= 0) {
sum1 -= sa[i] - sa[end+1];
sum2 -= sb[i] - sb[end+1];
}
}
long long answer = abs(sum1*maxB) + abs(sum2*maxA);
cout << answer << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
solve();
return 0;
}
No código, usamos sa e sb para armazenar a soma dos prefixos de a e b, diff armazena a diferença entre dois elementos adjacentes e pos armazena o ponto final esquerdo do intervalo que precisa ser invertido. Para atualizar maxSum, também precisamos considerar se o intervalo atualmente selecionado pode ser mesclado com intervalos adjacentes subsequentes para obter uma escolha melhor.
Finalmente, podemos realizar a otimização da soma do prefixo em a e b, respectivamente, para calcular rapidamente a soma do intervalo. Ao calcular a simpatia, também precisamos prestar atenção ao valor absoluto.
Em resumo, a complexidade de tempo deste algoritmo ganancioso é O(n), que é mais eficiente que os métodos de força bruta e métodos de programação dinâmica, e pode resolver problemas com grandes tamanhos de dados.
mmd, o código para a ideia de otimização não parece certo, está entorpecido.
2. Simule questões de criptografia e descriptografia
Pergunta:
Para uma string: kingsoft
, o vovô gosta de criptografá-la. O método de criptografia é pegar primeiro a cabeça, depois a cauda, até terminar. Depois de
criptografar de acordo com o método de criptografia do vovô, fica assim: ktifnogs
, agora o vovô transmitiu para você, você precisa descriptografar e gerar uma string.
Não pensei em nenhuma otimização para esta questão, então apenas inverti o método de criptografia para obtê-la.O que preciso prestar atenção é o problema de obter caracteres pares e ímpares, e nada mais;
3. Questões de programação dinâmica
esquecer