20260726

P2587 [ZJOI2008] 泡泡堂

这题真的只有绿吗。

思路

考虑如下贪心策略:

  • 如果当前我方最小打得过对方最小,那么就打。
  • 如果当前我方最大打得过对方最大,那么就打。(因为下面的操作是将对方最大的耗掉,我们既然能打过,那么为什么要耗掉呢?)。
  • 我方最小和对方最大玉碎,因为对方最大太强了,谁都打不过,最优的情况是用我方最小耗掉他。

我方最坏情况即对方最优情况,不用再分类一遍了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, a[N], b[N];
int work(int a[], int b[])
{
	int res = 0;
	int l = 1, r = n, mi = 1, ma = n;
	while(l <= r && mi <= ma)
	{
		if(a[mi] > b[l])
		{
			mi ++, l ++;
			res += 2;
		}
		else if(a[ma] > b[r])
		{
			ma --, r --;
			res += 2;
		}
		else
		{
			if(a[mi] == b[r]) res ++;
			mi ++, r --;
		}
	}
	return res;
}
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	for(int i = 1; i <= n; i ++) cin >> b[i];
	sort(a + 1, a  + n + 1);
	sort(b + 1, b + n + 1);
	cout << work(a, b) << " " << n * 2 - work(b, a) << endl;
}

P6927 [ICPC 2016 WF] Swap Space

思路

考虑将硬盘分成两类,然后排个序,贪心即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, ans, sum;
struct node
{
	int a, b;
};
vector<node> v1, v2;
bool cmp1(node a, node b) {return a.a < b.a;}
bool cmp2(node a, node b) {return a.b > b.b;}
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		int a, b; cin >> a >> b;
		if(a < b) v1.push_back({a, b});
		else v2.push_back({a, b});
	}
	sort(v1.begin(), v1.end(), cmp1);
	sort(v2.begin(), v2.end(), cmp2);
	for(int i = 0; i < v1.size(); i ++)
	{
		ans = min(ans, sum - v1[i].a);
		sum += (v1[i].b - v1[i].a);
	}
	for(int i = 0; i < v2.size(); i ++)
	{
		ans = min(ans, sum - v2[i].a);
		sum += (v2[i].b - v2[i].a);
	}
	cout << -ans << endl;
}

P8113 [Cnoi2021] 自我主义的平衡者

思路

考虑贪心。

不难发现,\(a_i\) 从小到大排序时,平均分是最高的。\(a_i\) 从大到小排序时,平均分是最低的。

证明:

\(a_i\) 从小到大排序:

  • 当前平均分还很低时,前面的 \(a_i\) 更愿意打满分,一旦打了满分,平均分就会被抬高。
  • 如果平均分已经超过他们的心理预期,他们就会打 \(0\) 分,且拉低平均分,方便后面的人打满分。
  • 因为数组已经升序排列,\(a_i\) 会越来越大。前面有人打 \(0\) 分压低平均分后,后面的 \(a_i\) 就更容易打满分。

\(a_i\) 从大到小排序:

  • 前面的 \(a_i\) 会将平均分拉的很高,那么后面的人就会几乎全部打 \(0\) 分,将平均分拉的很低。
  • 因为满分 \(m\) 是最高分,一旦前期把平均分抬高,后面再打 \(0\) 分也只能缓慢拉低,但很难再降到一个让挑剔者重新打满分的水平。这样满分就基本被“锁死”在前几个\(a_i\) 上,后续的人全在打 \(0\) 分,总分自然最小。

警示后人:

  • 小心精度问题。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans, a[100005], n, m, ans1;
signed main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i ++)
	{
		if(ans <= a[i] * (i - 1))
		{
			ans += m;
		}
	}
	for(int i = n; i >= 1; i --)
	{
		if(ans1 <= a[i] * (n - i))
		{
			ans1 += m;
		}
	}
	double cnt1 = ans * 1.0 / n, cnt2 = ans1 * 1.0 / n;
	printf("%.2lf %.2lf\n", cnt1, cnt2);
}

P1456 Monkey King

思路

直接启发式合并。

\(n\) 个优先队列,除了合并时小的合并到大的以外,其他的和暴力差不多。

注意需要开一个数组存储一个元素当前在的优先队列的编号。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
struct node
{
	int x, id;
	bool operator<(const node qwq) const
	{
		return qwq.x > x;
	}
};
priority_queue<node> q[N];
int n, cur[N], m, a[N];
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
		q[i].push({a[i], i});
		cur[i] = i;
	}
	cin >> m;
	while(m --)
	{
		int x, y; cin >> x >> y;
		int u = cur[x], v = cur[y];
		if(u == v)
		{
			cout << -1 << endl;
			continue;
		}
		if(v < u) swap(u, v);
		q[u].push({q[u].top().x / 2, q[u].top().id});
		q[u].pop();
		q[v].push({q[v].top().x / 2, q[v].top().id});
		q[v].pop();
		if(u <= v)
		{
			cur[x] = v;
			while(!q[u].empty())
			{
				q[v].push(q[u].top());
				cur[q[u].top().id] = v;
				q[u].pop();
			}
		}
		cout << q[v].top().x << endl;
	}
}

P1902 刺杀大使

思路

二分答案。

二分判断就dfs判断连通性就好了,如果一个点的值大于我们二分的那个答案,则不能走。

注意到dfs不能回溯,因为这个点一旦走不通,则没必要再走一遍。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, m, a[1005][1005], flag, vis[1005][1005];
int fx[4] = {0, 0, 1, -1}, fy[4] = {1, -1, 0, 0};
void dfs(int x, int y, int val)
{
	if(x == n)
	{
		flag = 1;
		return ;
	}
	if(flag == 1) return ;
	for(int i = 0; i < 4; i ++)
	{
		int xx = x + fx[i], yy = y + fy[i];
		if(!vis[xx][yy] && xx >= 1 && yy >= 1 && xx <= n && yy <= m && a[xx][yy] <= val)
		{
			vis[x][y] = 1;
			dfs(xx, yy, val);
			// vis[x][y] = 0;
		}
	}
}
bool check(int x)
{
	flag = 0;
	memset(vis, 0, sizeof(vis));
	dfs(1, 1, x);
	return flag;
}
signed main()
{
	cin >> n >> m;
	for(int i = 1; i <=  n; i ++)
	{
		for(int j = 1; j <= m; j ++)
		{
			cin >> a[i][j];
		}
	}
	int l = 0, r = 1000;
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(check(mid)) r = mid - 1;
		else l = mid + 1;
	}
	cout << r + 1 << endl;
}
posted @ 2026-07-26 13:16  Luowj  阅读(3)  评论(0)    收藏  举报