1 活动安排问题

至多多少个活动

https://oj.sdutacm.cn/onlinejudge3/contests/4287/problems/D

  1. 结构体数组的访问是no[i]不是no.fi[i]
  2. 因为按照字符串所以第二个比较的是id
  3. sort(a+1,a+1+n,cmp)记得+1和cmp,记得初始化now时把第一个节点也放进去。记得更新now
  4. cmp需要在结构体之后写
  5. vector是push_back,最后一个是ve.size() - 1。
  6. 比较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 ×

挤牛奶
会议安排
合并果子

  1. 挤牛奶需要用到pair,并且小根堆先按照first排序后按照second排序。所以第一个应该是fi,第二个是id
  2. 按照奶牛输入顺序输出,所以ans[no[i].id] = rid;,因为排完序后id会乱
  3. 注意冲突是<= 注意等号
  4. 第一个奶棚 的rid是1,不是牛的id
  5. 记得先ans++,再res[i]=ans;
  6. 记得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; //大根堆

image

点击查看代码
#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 汽车加油

oj原题

  1. 记得把数组开大点
  2. dist不是个数组
  3. 无解的条件是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

  1. 注意判断条件(a[i] > a[j]) 啥时候用dp啥时候用a
  2. 注意动态规划数组初始化
  3. 最长上升子序列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;
}

  1. 替换的作用是提升潜力

image

  1. 即使最后一次读取失败(没有数据了),n 也已经被加了 1。while (cin >> a[++n])

lower_bound,寻找第一个 “大于等于” 目标值的元素位置。
upper_bound寻找第一个 “严格大于” 目标值的元素位置

64b41676-3f18-4c4e-81c3-e05d7b3fda5f

  1. 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

  1. while (cin >> no[i1].pi >> no[i1].wi) 会乱,价值重量不对应。并且循环内要放到pw后
  2. 注意i1是数量而不是m,也没有n。sort要传对
  3. 要比较剩余的容量和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)部分背包问题

  1. printf不需要取地址!!!printf("%.2f", ans);
  2. 取地址的是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 合并石子(哈夫曼树)

  1. 需要(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;
}
posted on 2025-12-04 22:40  Hoshino1  阅读(5)  评论(0)    收藏  举报