本篇题解只是记录我的做题过程代码,不提供最优解
(另外这里所有的时间复杂度都只分析单个样例,不算
t
t
t的时间复杂度)
A
点击此处查看对应的题目.
本题设计算法:贪心 + 位运算
将每个数拆开为2进制,统计所有数的二进制位置1多还是0多,然后根据题意取出较多的那个为作为答案位
贪心策略:对每位考虑,如果该位1多,就设为1,反之为设为0
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int,int> PII;
const int N = 10010;
int n,l;
void split_sum(int x,int index,vector<PII> &v)
{
while (x){
if (x % 2) v[index].second ++;
else v[index].first ++;
index ++;
x >>= 1;
}
while (index < l && !x) v[index ++].first ++;
}
int main()
{
int t;
cin >> t;
while (t -- ){
cin >> n >> l;
vector<PII> cnt(N,{0,0});
vector<int> num;
for (int i = 0;i < n;i ++ ) {
int x;
cin >> x;
split_sum(x,0,cnt);
}
for (int i = 0;i < l;i ++ ){
if (cnt[i].first >= cnt[i].second) num.push_back(0);
else num.push_back(1);
}
int bit = 1,sum = 0;
for (int i = 0;i < num.size();i ++) {
sum += num[i] * bit;
bit <<= 1;
}
cout << sum << '\n';
}
return 0;
}
B
点击此处查看对应的题目.
本题设计算法:贪心
假如我们找到了两个相同的数,设分别在
l
,
r
l, r
l,r,要求框出这段范围,那么这个范围的最大值就是
l
l
l 前面的数加上
r
r
r后面的数并加上该数,所以要让 (l - 1) + (n - r) + 1 最大,我这里通过pair存一下相同数的下标, 遍历取最大即可。
贪心策略:通过排序快速找到相同数值的下标
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 3e5 + 10,INF = 1e9 + 7;
typedef pair<int, int> PII;
PII a[N];
bool st[N];
int main()
{
int t;
cin >> t;
while (t -- ){
int n;
cin >> n;
for (int i = 1;i <= n;i ++ ) {
cin >> a[i].first;
a[i].second = i;
}
sort(a + 1,a + n + 1);
int maxn = -INF;
for (int i = 1;i < n;i ++ ) {
if (a[i].first == a[i + 1].first ) {
int x = (a[i].second - 1) + (n - a[i + 1].second) + 1;
maxn = max(maxn , x);
}
}
if (maxn == -INF) puts("-1");
else cout << maxn << '\n';
}
return 0;
}
C
点击此处查看对应的题目.
本题设计算法:动态规划
状态表示:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示以第 i 个标识牌为结尾且拆了 j 个限速牌花费的最少时间,其中 第一个标识牌不拆。
状态转移: d p [ i ] [ j ] = m i n p r e = 0 j ( d p [ i ] [ j ] , d p [ i − p r e − 1 ] [ j − p r e ] + ( s [ i ] − s [ i − p r e − 1 ] ) ∗ f r a v [ i − p r e − 1 ] ) dp[i][j]=min^j_{pre = 0}(dp[i][j],dp[i - pre - 1][j - pre] + (s[i] - s[i - pre - 1]) * frav[i - pre - 1]) dp[i][j]=minpre=0j(dp[i][j],dp[i−pre−1][j−pre]+(s[i]−s[i−pre−1])∗frav[i−pre−1]) ————其中frav表示速度的倒数,pre是前面的可保留的标识牌
状态转移也可以用状态划分来表示:
转化为01背包问题就好理解了(本质上都是有代价的最优组合问题)
时间复杂度 O ( n 3 ) O(n^3) O(n3)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010,INF = 0x3f3f3f3f;
int s[N],fra_v[N],dp[N][N];
int n,l,k;
int main()
{
cin >> n >> l >> k;
for (int i = 1;i <= n;i ++ ) cin >> s[i];
for (int i = 1;i <= n;i ++ ) cin >> fra_v[i];
s[n + 1] = l;
memset(dp,0x3f,sizeof dp);
dp[1][0] = 0;
for (int i = 2;i <= n + 1;i ++){
for (int j = 0;j <= k;j ++ ) {
dp[i][j] = dp[i - 1][j] + (s[i] - s[i - 1]) * fra_v[i - 1];//先把之前的状态继承过来
//必然拔掉一个,所以从1枚举
for (int pre = 1;pre <= j;pre ++ ) {
if (i - pre - 1 <= 0) break;
dp[i][j] = min(dp[i][j],dp[i - pre - 1][j - pre] + (s[i] - s[i - pre - 1]) * fra_v[i - pre - 1]);
}
}
}
int res = INF;
for (int i = 0;i <= k;i ++ ) res = min(res,dp[n + 1][i]);
cout << res << '\n';
return 0;
}