1 活动安排问题
至多多少个活动
https://oj.sdutacm.cn/onlinejudge3/contests/4287/problems/D
- 结构体数组的访问是no[i]不是no.fi[i]
- 因为按照字符串所以第二个比较的是id
- sort(a+1,a+1+n,cmp)记得+1和cmp,记得初始化now时把第一个节点也放进去。记得更新now
- cmp需要在结构体之后写
- vector是push_back,最后一个是ve.size() - 1。
- 比较no[i].st记得大于号还是小于号,记得能取等
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
struct node {
int id;
int st = 0;
int fi = 0;
}no[maxn];
bool cmp(node a, node b)
{
//比较特别的是这里比较的是id
if (a.fi == b.fi)return a.id < b.id;
return a.fi < b.fi;
}
int main(){
int n;
cin >> n;
for (int i = 1;i <= n;i++)
{
no[i].id = i;
cin >> no[i].st >> no[i].fi;
}
//忘记传cmp
sort(no + 1, no + 1 + n,cmp);
vector<int> ve;
ve.push_back(no[1].id);
int cou = 1;
int now = no[1].fi;
for (int i = 2;i <= n;i++)
{
//贪心写反了
if (no[i].st >= now)
{
cou++;
now = no[i].fi;
ve.push_back(no[i].id);
}
}
for (int i = 0;i < ve.size();i++)
{
cout << ve[i];
if (i != ve.size()-1) cout << ",";
//应该是size-1.
}
}
至少多少个房间 1207 ×
- 挤牛奶需要用到pair,并且小根堆先按照first排序后按照second排序。所以第一个应该是fi,第二个是id
- 按照奶牛输入顺序输出,所以ans[no[i].id] = rid;,因为排完序后id会乱
- 注意冲突是<= 注意等号
- 第一个奶棚 的rid是1,不是牛的id
- 记得先ans++,再res[i]=ans;
- 记得sort
涉及小根堆,大根堆
1208
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
int n;
int a[maxn];
int now = 0;
vector<int> ve;
int ans = 0;
int res[maxn];
struct node {
int id;
int st;
int fi;
}no[maxn];
signed main()
{
int n;
cin >> n;
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>>q;
for (int i = 1;i <= n;i++)
{
no[i].id = i;
cin >> no[i].st >> no[i].fi;
}
for (int i = 1;i <= n;i++)
{
if (!q.empty()&&q.top().first<=no[i].st)
{
int rid = q.top().second;
q.pop();
q.push({ no[i].fi,rid });
res[no[i].id] = rid;
}
else {
ans++;
now = no[i].fi;
q.push({now,ans});
res[no[i].id] = ans;
}
}
cout << ans << endl;
for (int i = 1;i <= n;i++)
{
cout << res[i] << endl;
}
}
priority_queue<int, vector<int>, greater<int>> q1; //小根堆
priority_queue<int, vector<int>> q2; //大根堆

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
int ans[maxn];
int cou;
struct node {
int id;
int st;
int fi;
}no[maxn];
bool cmp(node n1, node n2)
{
if (n1.st == n2.st)return n1.fi < n2.fi;
return n1.st < n2.st;
}
int main()
{
int n;
cin >> n;
for (int i = 1;i <= n;i++)
{
cin >> no[i].st >> no[i].fi;
no[i].id = i;
}
//记得+1,还有for循环的
sort(no +1, no + 1 + n,cmp);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
for (int i = 1;i <= n;i++)
{
//不能取等
if (!q.empty() && q.top().first < no[i].st)
{
int rid = q.top().second;
q.pop();
//对于pair变量的push需要加大括号{}
q.push({ no[i].fi, rid });
//需要记录答案
ans[no[i].id] = rid;
}
else
{
cou++;
q.push({ no[i].fi, cou });
ans[no[i].id] = cou;
}
}
cout << cou << endl;
for (int i = 1;i <= n;i++)
{
cout << ans[i] << endl;
}
}
2 汽车加油
- 记得把数组开大点
- dist不是个数组
- 无解的条件是a[i]>n
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5010;
int a[maxn];
int ans = 0;
int main()
{
int n, k;
//n是容量,k是加油站数量
//因为我们在 cdist 即将超过 n 之前就进行了重置(加油),所以 cdist 永远不可能在进入循环的开头时大于 n。
cin >> n >> k;
int cdist = 0;
//注意是k+1.
for (int i = 1;i <= k+1;i++)
cin >> a[i];
for (int i = 1;i <= k+1;i++)
{
if (a[i] > n)
{
cout << "No Solution!" << endl;
return 0;
}
else if (cdist + a[i] > n)
{
cdist = a[i];
ans++;
}
else {
cdist += a[i];
}
}
cout << ans << endl;
}
3 导弹拦截
补充:最长上升子序列
https://www.luogu.com.cn/problem/B3637
- 注意判断条件(a[i] > a[j]) 啥时候用dp啥时候用a
- 注意动态规划数组初始化
- 最长上升子序列dp[]是以i为结尾的最长的,所以我们还需要比较哪个结尾最大!而不是直接输出dp[n];
最长上升子序列
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5010;
int a[maxn];
int dp[maxn];
int n;
int main()
{
int n;
cin >> n;
for (int i = 0;i < n;i++)cin >> a[i];
for (int i = 0;i < n;i++)
{
dp[i] = 1;
//以第 i 个数字 a[i] 结尾 的最长上升子序列的长度
//长度至少为1
for (int j = 0;j < i;j++)
{
if (a[i] > a[j])
{
dp[i] = max(dp[j] + 1, dp[i]);
//i可以接在j后面
//对于第 i 个数字 a[i] 来说,它前面可能有 多个 比它小的数字 a[j] 都可以作为它的“上一个台阶”。
//我们需要在这些所有的可能性中,选出一个最长的。
}
}
}
int res = 0;
for (int i = 0;i < n;i++)
{
res = max(res, dp[i]);
}
cout << res;
}
点击查看代码
using namespace std;
const int maxn = 5010;
int a[maxn];
int dp[maxn];
int main()
{
int n;
cin >> n;
for (int i = 0;i < n;i++)cin >> a[i];
for (int i = 0;i < n;i++)
{
dp[i] = 1;
//长度至少为1
for (int j = 0;j < i;j++)
{
if (a[i] > a[j])
{
dp[i] = max(dp[j] + 1, dp[i]);
}
}
}
int res = 0;
for (int i = 0;i < n;i++)
{
res = max(res, dp[i]);
}
cout << res;
}
如果你能在一个序列中找到一个长度为 $K$ 的 上升子序列(每个都比前一个高),那么这 $K$ 枚导弹 绝对不可能 共用同一套系统。你至少需要 $K$ 套系统
n^2解 50%
#include<bits/stdc++.h>
using namespace std;
// 题目中说前50%数据在10^4以内,为了防止数组越界,这里开大一点
const int maxn = 100005;
int a[maxn];
int dp[maxn];
int main()
{
int n = 0;
// 题目输入格式是一行若干个整数,没有直接给出n,所以要这样读入
while (cin >> a[n]) {
n++;
}
// 第一问:最长不上升子序列 (也就是一套系统最多能拦截多少)
// 判定条件:后面比前面小或者相等 (a[i] <= a[j])
int res1 = 0;
for (int i = 0; i < n; i++)
{
dp[i] = 1; // 长度至少为1
for (int j = 0; j < i; j++)
{
if (a[i] <= a[j]) // 注意这里是不上升:小于等于
{
dp[i] = max(dp[j] + 1, dp[i]);
}
}
res1 = max(res1, dp[i]);
}
// 第二问:最少需要配备多少套系统
// 根据Dilworth定理,这等价于求最长上升子序列
// 判定条件:后面比前面大 (a[i] > a[j]),这正是你原本的代码逻辑
int res2 = 0;
for (int i = 0; i < n; i++)
{
dp[i] = 1; // 重置dp数组,再次计算
for (int j = 0; j < i; j++)
{
if (a[i] > a[j]) // 这里是上升:大于
{
dp[i] = max(dp[j] + 1, dp[i]);
}
}
res2 = max(res2, dp[i]);
}
cout << res1 << endl;
cout << res2 << endl;
return 0;
}
- 替换的作用是提升潜力

- 即使最后一次读取失败(没有数据了),n 也已经被加了 1。while (cin >> a[++n])
lower_bound,寻找第一个 “大于等于” 目标值的元素位置。
upper_bound寻找第一个 “严格大于” 目标值的元素位置

- int p = upper_bound(d1.begin(), d1.end(), a[i], greater
()) - d1.begin();
这里是直接传入greater()
1208
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
vector<int> ve1;
vector<int> ve2;
int n;
int main()
{
while (cin >> a[++n])
{ }
ve1.push_back(a[1]);
n -= 1;
for (int i = 2;i <= n;i++)
{
if (a[i] <= ve1.back())
{
ve1.push_back(a[i]);
}
else {
int idx = upper_bound(ve1.begin(),ve1.end(), a[i],greater<int>()) - ve1.begin();
ve1[idx] = a[i];
}
}
ve2.push_back(a[1]);
for (int i = 2;i <= n;i++)
{
if (a[i] > ve2.back())
{
ve2.push_back(a[i]);
}
else {
int idx = lower_bound(ve2.begin(), ve2.end(), a[i]) - ve2.begin();
ve2[idx] = a[i];
}
}
cout << ve1.size() << endl;
cout << ve2.size() << endl;
}
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int a[maxn];
vector<int> d1, d2;
int main() {
int n = 0;
while (cin >> a[n]) {
n++;
}
// 第一问:最长不上升子序列
if (n > 0) {
d1.push_back(a[0]);
for (int i = 1; i < n; i++) {
if (a[i] <= d1.back()) {
d1.push_back(a[i]);
} else {
// 找第一个严格小于 a[i] 的位置,替换掉它
int p = upper_bound(d1.begin(), d1.end(), a[i], greater<int>()) - d1.begin();
d1[p] = a[i];
}
}
}
// 第二问:最长上升子序列
if (n > 0) {
d2.push_back(a[0]);
for (int i = 1; i < n; i++) {
if (a[i] > d2.back()) {
d2.push_back(a[i]);
} else {
// 找第一个大于等于 a[i] 的位置,替换掉它
int p = lower_bound(d2.begin(), d2.end(), a[i]) - d2.begin();
d2[p] = a[i];
}
}
}
cout << d1.size() << endl;
cout << d2.size() << endl;
return 0;
}
4 可拆分背包
1)装船问题
https://oj.sdutacm.cn/onlinejudge3/contests/4287/problems/C
- while (cin >> no[i1].pi >> no[i1].wi) 会乱,价值重量不对应。并且循环内要放到pw后
- 注意i1是数量而不是m,也没有n。sort要传对
- 要比较剩余的容量和m,now-m
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 10;
int a[maxn];
int now = 0;
vector<int> ve;
int ans = 0;
int res[maxn];
struct node {
int pi;
int wi;
int pw;
}no[maxn];
bool cmp(node a, node b)
{
if (a.pw == b.pw)
return a.wi > b.wi;
return a.pw > b.pw;
}
signed main()
{
int m;
cin >> m;
int i1 = 1;
//能这样++,会乱,价值重量不对应。并且循环内要放到pw后
//注意i1是数量而不是m
while (cin >> no[i1].pi >> no[i1].wi)
{
no[i1].pw = no[i1].pi / no[i1].wi;
i1++;
}
i1--;
//cout << i1 << endl;
sort(no + 1, no +1+ i1, cmp);
int vav = 0;
int now = 0;
for (int i = 1;i <= i1;i++)
{
if (m-now >= no[i].wi)
{
vav += no[i].pi;
now += no[i].wi;
}
else {
vav += no[i].pw * (m - now);
break;
}
}
cout << vav << endl;
}
2)部分背包问题
- printf不需要取地址!!!printf("%.2f", ans);
- 取地址的是scanf
https://www.luogu.com.cn/problem/P2240
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
struct g {
float m = 0;
float v = 0;
}gold[maxn];
bool cmp(g x, g y)
{
return (x.v / x.m) > (y.v / y.m);
//除号写成小于号
}
int main()
{
int n, t;
cin >> n >> t;
for (int i = 1;i <= n;i++)
{
cin >> gold[i].m >> gold[i].v;
}
sort(gold + 1, gold + 1 + n,cmp);
float ans = 0;
float now = 0;
for (int i = 1;i <= n;i++)
{
if (gold[i].m <= (t-now))
{
ans += gold[i].v;
now += gold[i].m;
}
else
{
//要使用浮点数而不是int
float mv = gold[i].v / gold[i].m;
ans += (t - now) * mv;
break;
//忘记break
}
}
//输出的是浮点数
printf("%.2f", ans);
}
5 合并石子(哈夫曼树)
- 需要(n-1)%(k-1)
https://oj.sdutacm.cn/onlinejudge3/contests/4287/problems/B
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, k;
int ansmin = 1000;
int ansmax = -1000;
const int maxn = 1e5 + 10;
int a[maxn];
signed main()
{
cin >> n >> k;
priority_queue<int, vector<int>, greater<int>> q1;
priority_queue<int, vector<int>> q2;
for (int i = 0;i < n;i++)
{
int x;
cin >> x;
q1.push(x);
q2.push(x);
}
//每次减少的步长是:$k - 1$。需要减少的总数是:$n - 1$。
if (k > 2) {
// (n-1) % (k-1) 只要不为0,就补0
while ((q1.size() - 1) % (k - 1) != 0) {
q1.push(0);
}
}
//没有累加花费,队列里其实不是,只是重量和
int total1 = 0;
int total2 = 0;
while (q1.size()>1)
{
int sum = 0;
for (int i = 1;i <= k;i++)
{
sum += q1.top();
q1.pop();
}
total1 += sum;
q1.push(sum);
}
while (q2.size() > 1)
{
int sum = 0;
for (int i = 1;i <= 2;i++)
{
sum += q2.top();
q2.pop();
}
total2 += sum;
q2.push(sum);
}
cout << total2 << " " << total1;
return 0;
}
浙公网安备 33010602011771号