BUAASOFT1721算法上机期末部分题解

从1721开始,软院的算法上机期末要考三次,这是最后一次上机考的部分题解

电池的寿命

  • 题目大意
    在这里插入图片描述

  • 解题思路

    这个题我们可以采用分类讨论的策略

    1. 如果一组电池中电量最大的那个的电量比其他所有电池的电量加起来都多,那么显然我们可以一直使用这个电量最大的电池然后不停的更换其他电量小的电池。最后我们得到的最长使用时间显然是其他小电量电池电量之和。

    2. 如果不是上述情况呢,就像题目中第二个样例,可以发现电池最后其实是全用完了的。我们猜测这种情况下所有电池电量可以全部用完,现给出简要证明

      我们可以将所有电池按电量大小排序,然后每次用最大电量的电池和电量小的电池一起使用一小时(电量小的电池的使用顺序也从大到小),我们可以发现使用到最后只有三种情况

      1. 剩下两个电池,电量都为1
      2. 剩下三个电池,电量分别为2,1,1
      3. 剩下三个电池,电量全为1

      这三种情况很明显电量都可以全部用完,所以使用时间显然是总电量的一半

  • 参考代码

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<set>
    #include<map>
    #include<iomanip>
    using namespace std;
    int n;
    typedef long long ll;
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("Text.txt", "r", stdin);
    #else
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cout.tie(0);
    #endif // !ONLINE_JUDGE
    	
    	while (cin >> n) {
    		int sum = 0;
    		int top = 0;
    		int power = 0;
    		for (int i = 0; i < n; i++) {
    			cin >> power;
    			sum += power;
    			top = max(top, power);
    		}
    		if (top >= sum - top) cout << fixed << setprecision(1) << 1.0*sum - 1.0*top << endl;
    		else cout << fixed << 1.0*sum / 2.0 << endl;
    	}
    	return 0;
    }
    

岛屿

  • 题目大意

    ![在这里插入图片描述](https://img-

  • 解题思路

    其实这个题是个脑经急转弯,画画图就知道答案就是1或者2!!!我们只需要判断是一个点还是两个点就行。判断方法如下,我们把这些点中最小,最大的 x x x, y y y给找出来,然后如果在这些点中有某个点符合下面这四种情况,那么答案为1,只要一个点就可以覆盖

    1. 点的 x x x坐标为最小的 x x x y y y坐标为最小的 y y y
    2. 点的 x x x坐标为最小的 x x x y y y坐标为最大的 y y y
    3. 点的 x x x坐标为最大的 x x x y y y坐标为最小的 y y y
    4. 点的 x x x坐标为最大的 x x x y y y坐标为最大的 y y y

    其他情况答案为2,下面给出两个示例图

    在这里插入图片描述

  • 参考代码

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<set>
    #include<map>
    #include<iomanip>
    using namespace std;
    int n;
    typedef long long ll;
    const int MAXN = 1e5 + 5;
    const int zinf = 0x3f3f3f3f;
    const int finf = -0x3f3f3f3f;
    struct Point {
    	int x, y;
    }P[MAXN];
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("Text.txt", "r", stdin);
    #else
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cout.tie(0);
    #endif // !ONLINE_JUDGE
    	int T;
    	cin >> T;
    	while (T--) {
    		cin >> n;
    		for (int i = 0; i < n; i++)
    			cin >> P[i].x >> P[i].y;
    		int minx = zinf;
    		int maxx = finf;
    		int miny = zinf;
    		int maxy = finf;
    		bool flag = false;
    		for (int i = 0; i < n; i++) {
    			if ((P[i].x==maxx&&P[i].y==maxy)||(P[i].x == minx && P[i].y == maxy)||(P[i].x == maxx && P[i].y == miny)||(P[i].x == minx && P[i].y == miny)) {
    				cout << "1\n";
    				flag = true;
    				break;
    			}
    		}
    		if (!flag) cout << "2\n";
    	}
    	
    	return 0;
    }
    

地铁建设

  • 题目大意

    在这里插入图片描述

  • 解题思路

    ALS变式,但是这里的限制条件是不能是上次用过的模式,所以我们遍历一遍上一层循环中除了与这一层下标相等的所有元素,更新所得的最小费用即可

  • 参考代码

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<set>
    #include<map>
    #include<iomanip>
    using namespace std;
    int n;
    typedef long long ll;
    const int MAXN = 200005;
    int dp[MAXN][3];
    int G[MAXN][3];
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("Text.txt", "r", stdin);
    #else
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cout.tie(0);
    #endif // !ONLINE_JUDGE
    	cin >> n;
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < 3; j++) {
    			cin >> G[i][j];
    			dp[i][j] = 0x3f3f3f3f;
    		}
    	}
    	for (int i = 0; i < 3; i++)
    		dp[0][i] = G[0][i];
    	for (int i = 1; i < n; i++) {
    		for (int j = 0; j < 3; j++) {
    			for (int k = 0; k < 3; k++) {
    				if (k != j)
    					dp[i][k] = min(dp[i][k], dp[i - 1][j] + G[i][k]);
    			}
    		}
    	}
    	int minn = 0x3f3f3f3f;
    	for (int i = 0; i < 3; i++) {
    		if (dp[n - 1][i] < minn)
    			minn = dp[n - 1][i];
    	}
    	cout << minn << "\n";
    	return 0;
    }
    

SkyLee是哥布林杀手

  • 题目大意

在这里插入图片描述在这里插入图片描述

  • 解题思路

    看到这个题目我有种似曾相识的感觉,这种杀怪得经验得题目,感觉很像背包问题呀!之后我注意到题面上说的击杀一把哥布林可以用多把武器,然后还有说要满足一定条件才能使用武器,然后可以击杀很多只哥布林,这不就是一个完全背包吗!只不过要满足一定限制条件才能把背包放进物体并且背包得容量是时刻在变化的。我们把耐久度看作容量,然后等级看作第一等级的价值,金钱看作第二等级的价值,只需要在更新等级的时候顺带更新金钱数我们就可以得到最后的结果。每次打怪后我们重置背包容量,将新的能杀死的哥布林加入可选的物品序列中,然后每次将所得的最大经验和金钱相加,最后输出即可!

  • 参考代码

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<set>
    #include<map>
    #include<iomanip>
    using namespace std;
    int n;
    typedef long long ll;
    int w, l, s;
    const int MAXN = 1e6 + 7;
    //用pair第一排序方便,第二不用定义多个struct
    pair<int, int> weapon[MAXN], value[MAXN], dp[MAXN];
    int weight[MAXN];
    //完全背包
    pair<int, int>Completebackpack(int Weight) {
    	for (int i = 0; i <= Weight; i++)
    		dp[i] = make_pair(0, 0);
    	for (int i = 1; i <= s; i++) {
    		for (int j = weight[i]; j <= Weight; j++) {
    			pair<int,int> tmp=make_pair(dp[j - weight[i]].first + value[i].first,dp[j - weight[i]].second + value[i].second);
    			dp[j] = max(dp[j], tmp);
    		}
    	}
    	return dp[Weight];
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("Text.txt", "r", stdin);
    #else
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cout.tie(0);
    #endif // !ONLINE_JUDGE
    	while (cin >> w >> l >> s) {
    		for (int i = 1; i <= w; i++)
    			cin >> weapon[i].first >> weapon[i].second;
    		for (int i = 1; i <= s; i++)
    			cin >> value[i].first >> weight[i] >> value[i].second;
    		sort(weapon + 1, weapon + 1 + w);
    		pair<int, int> ans = make_pair(l, 0);
    		int rl = 0;
    		int i = 1;
    		while (i <= w) {
    			while (i <= w && weapon[i].first <= ans.first) 
    				rl += weapon[i++].second;
    			if (rl > 0) {
    				pair<int, int>tmp = Completebackpack(rl);
    				ans.first += tmp.first;
    				ans.second += tmp.second;
    				rl = 0;
    			}
    			else i++;
    		}
    		cout << ans.first << " " << ans.second << "\n";
    	}
    	return 0;
    } 
    

花落何处寻

  • 题目大意

    在这里插入图片描述
    在这里插入图片描述

  • 解题思路

    有一个关于直径的结论:

    从一个点出发,不重复经过节点,如果想要走的路程最远,那么最终到达的点一定是树的直径的某个端点

    这道题中我们可以考虑,设它可到达的节点为 c a n r e a c h [ i ] ​ canreach[i]​ canreach[i],一共有 k ​ k​ k个,若到达 c a n r e a c h [ i ] ​ canreach[i]​ canreach[i]的距离在所有它能到达的节点中最大,则节点 i ​ i​ i一定是能到达的点中,两两距离最远的一对点中的一个 ( o p , e d ) ​ (op,ed)​ (op,ed)

    也就是求所有 c a n r e a c h [ i ] canreach[i] canreach[i]构成的一棵树的直径,方法如下:

    先任意找一个 c a n r e a c h [ i ] canreach[i] canreach[i],从它开始dfs1一遍找出最远的点 o p op op,再从 o p op op开始,的dfs1一遍找出 e d ed ed

    之后从 o p op op e d ed ed分别做一次dfs2,给每个距离不超过 d d d的点的 c n t cnt cnt加上1,最后统计 c n t cnt cnt为2的点的个数就是答案了。

  • 参考代码

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<set>
    #include<map>
    #include<iomanip>
    using namespace std;
    int n, m, d;
    int maxd;
    int op, ed;
    const int MAXN = 1e6 + 5;
    int aff[MAXN];
    int cnt[MAXN];
    bool flag[MAXN];
    vector<int> edge[MAXN];
    void dfs1(int now, int p, int nd, int &v)
    {
    	if (nd>maxd && flag[now])
    	{
    		maxd = nd;
    		v = now;
    	}
    	for (int i = 0; i<edge[now].size(); i++)
    	{
    		int temp = edge[now][i];
    		if (temp != p)
    		{
    			dfs1(temp, now, nd + 1, v);
    		}
    	}
    	}
    
    void dfs2(int now, int p, int stp)
    {
    	if (stp>d) return;
    	cnt[now]++;
    	for (int i = 0; i<edge[now].size(); i++)
    	{
    		int temp = edge[now][i];
    		if (temp != p) dfs2(temp, now, stp + 1);
    	}
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("Text.txt", "r", stdin);
    #else
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cout.tie(0);
    #endif // !ONLINE_JUDGE
    	cin >> n >> m >> d;
    	memset(flag, false, sizeof(flag));
    	for (int i = 1; i <= m; i++) {
    		cin >> aff[i];
    		flag[aff[i]] = true;
    	}
    	for (int i = 1; i < n; i++) {
    		int x, y;
    		cin >> x >> y;
    		edge[x].push_back(y);
    		edge[y].push_back(x);
    	}
    	maxd = -1;
    	dfs1(aff[1], -1, 0, op);
    	maxd = -1;
    	dfs1(op, -1, 0, ed);
    	memset(cnt, 0, sizeof(cnt));
    	dfs2(op, -1, 0);
    	dfs2(ed, -1, 0);
    	int ans = 0;
    	for (int i = 1; i <= n; i++)
    		if (cnt[i] == 2) ans++;
    	cout << ans << endl;
    	return 0;
    }
    

    参考博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值