codeforces 798 补题计划 DE

D:本来是想跑图求多源bfs最短路的最大值,结果发现这个更根本跑不出来(对我来说),最后看完题解才发现这可以贪心。。。

这道题给我的启发在于,多源bfs求最短路的最大值这种算法可能并不存在,要是之后遇见类似的题就直接换思路吧,同时这种求最长距离的思路也类似与一颗树上每个点距离哪些点的距离最长,我们可以发现,一个点距离最长的那个点是树的直径中的一个

我们思考对于任意一个点到B的最短距离的最大值可能位于哪些地方?对于一个二维图来讲,我们假如想变得离一个点越来越远,我们可以怎么做,那就是靠到墙角,即4个离角落最近的距离,此时我们已经确定了找的目标,现在就是如何去查找,考虑假如现在一个点固定位置,我们位于左上角的点如何远离他呢,是不是向左上靠,那结果就是x减小或y减小,因为曼哈顿距离下,这两者的贡献是一样的,所以我们可以找x+y最小的点,他就是左上角(此时图(1,1)左上),右下同理,

对于左下:此时距离增大结果是:x++,y--那么我们维护一个y-x求他最小就好了,对于右上同理,维护一个x-y;

 代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 3010;
pair<int, int>a[4];//用来储存4个角落
signed main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		cin >> n >> m;
		bool flag[4] = { 0,0,0,0 };
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				char s;
				cin >> s;
				if (s == 'B')
				{
					if (!flag[0] || i + j < a[0].first + a[0].second)//左上
					{
						flag[0] = 1; a[0].first = i, a[0].second = j;
					}
					if (!flag[1] || i + j > a[1].first + a[1].second)
					{
						flag[1] = 1; a[1].first = i, a[1].second = j;
					}
					if (!flag[2] || j - i < a[2].second - a[2].first)
					{
						flag[2] = 1; a[2].first = i, a[2].second = j;
					}
					if (!flag[3] || i - j < a[3].first - a[3].second)
					{
						flag[3] = 1; a[3].first = i, a[3].second = j;
					}
				}
			}
		}
		int ans =1e9;
		int idx, idy;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				int h = 0;
				for (int k = 0; k < 4; k++)
				{
					h = max(h, abs(i - a[k].first) + abs(j - a[k].second));
				}
				if (h < ans)
				{
					ans = h;
					idx = i, idy = j;
				}
			}
		}
		cout << idx << ' ' << idy<<endl;
	}
}

E:是我目前实力无法高攀的题:

考虑做法,可以比较明确这题和二进制与并查集有关;

先考虑并查集的运用,对于任意一个数,我们贪心的想,一次变化就是要让其可以与更多的数经行相连接,现在就是考虑如何最大化。

同时我们考虑与&有关的二进制知识,可以发现有lowbit,考虑lowbit的含义,一个数的二进制最小1的位数,现在考虑如何运用, 我们对一个加一减一试试看会发生什么,我们发现,加一后这个数假如是偶数,lowbit并不会变,假如是奇数这个lowbit会变小为2,对于-1,我们发现,这个数的lowbit以下的所有二进制均会变为1,这就代表这个数可以和所有lowbit小于他的数进行相连,这个正是与题目有关的操作,同时考虑对于奇数,他的话会更新lowbit

那么有了-1的神奇操作,我们可以维护一个最大的lowbit,将其-1,此时会将所有lowbit小于他的数与其相连接,假如此时有多个lowbit最大呢,对于其他的来讲我们将其全部-1虽然可以,但可能并不是最优解,考虑+1这个操作有什么用,假如我们对其中一个经行+1,会发现如果此时其lowbit不为1的话,我们可以将以其为中转站,将所有lowbit最大的数连向他,同时将其与-1的最大lowbit联系起来,此时一定连通。

这情况是在lowbit最大不为1的时候,考虑最大为1如何处理?

起始不难发现,我们处理0的时候一定会将其变为1,此时假如已经连通的话就没有必要再考虑以上的情况了,假如不连通的话,什么此时一定有lowbit>1的数,那么以上那个情况就不存在!

当实际情况是还是有没有考虑清楚的情况,假如我们有1 1 1 4 12 1 1 这种情况,会发现此时只要4+1就好,不用再4 -1 12 +1 不够总的来说上述是最小解的最大可能,所以我们只要暴力跑一次将所有数-1/+1 假如法成功,我们就跑上述情况就好了

所以代码如下:

//那么并查集的部分是利用二进制优化的

#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
const int maxn =3e3 + 10;
int  fa[maxn];
int a[maxn], n;
int lowbit(int x)
{
	return x & (-x);
}
int find(int x)
{
	if (x == fa[x])return x;
	else
		return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
	x = find(x), y = find(y);
	if (x != y)
		fa[x] = y;
}
bool check()
{
	for (int i = 1; i <= n + 31; i++)fa[i] = i;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= 30; j++)
		{
			if ((a[i] >> j) & 1)
			{
				merge(n+ j+1, i);
			}
		}
	}
	int cnt = 0;
	for (int i = 1; i <= n; i++)
	{
		if (find(i) == i)
			cnt++;
	}
	if (cnt == 1)
		return 1;
	else
		return 0;
}
void solve()
{
	cin >> n;
	int maxlow = 0;
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		if (a[i] == 0)
		{
			ans++, a[i]++;
		}
		maxlow = max(maxlow, lowbit(a[i]));
	}

	if (check())
	{
		cout << ans << endl;
		for (int i = 1; i <=n; i++)
		{
			cout << a[i] << ' ';
		}
		cout << endl;
		return;
	}
	for (int i = 1; i <= n; i++)
	{
		a[i]--;
		if (check())
		{
			cout << ans + 1 << endl;
			for (int i = 1; i <= n; i++)
			{
				cout << a[i] << ' ';
			}
			cout << endl;
			return;
		}
		a[i]++;
	}
	for (int i = 1; i <= n; i++)
	{
		a[i]++;
		if (check())
		{
			cout << ans + 1 << endl;
			for (int i = 1; i <= n; i++)
			{
				cout << a[i] << ' ';
			}
			cout << endl;
			return;
		}
		a[i]--;
	}
	//第三次
	for (int i = 1; i <= n; i++)
		if (maxlow == lowbit(a[i]))
		{
			a[i]--;
				break;
		}
	for(int i=1;i<=n;i++)
		if (maxlow == lowbit(a[i]))
		{
			a[i]++;
			break;
		}
	cout << ans + 2 << endl;
	for (int i = 1; i <= n; i++)
		cout << a[i] << ' ';
	cout << endl;
}

signed main()
{
	int t;
	cin >> t;
	while (t--)
		solve();
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces的动态规划题单中,有基础DP、优化递推式、进阶DP、数据结构、优先队列、并查集、图论、最短路、最小生成树、数论、二分搜索等不同类型的题目。 代码中的内容是一个动态规划的例子,它采用了一个二维数组来存储中间结果,并通过递推的方式计算最优解。这个例子中,它使用了一个for循环嵌套来遍历数组,并利用状态转移方程更新数组中的值。最后输出的是计算得到的最优解。 要注意的是,这段代码是一个完整的程序,需要依赖于一些特定的输入数据才能正确运行。如果你想在Codeforces上找到更多的动态规划题目,可以访问它们的官方网站并浏览题库。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [牛客练习_21314:codeforces (动态规划+01背包)](https://blog.csdn.net/qq_45750296/article/details/109587967)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [leetcode双人赛-acm-challenge-workbook:acm-挑战-工作簿](https://download.csdn.net/download/weixin_38701340/19923844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round #750 (Div. 2)E题(动态规划)](https://blog.csdn.net/m0_51506743/article/details/121083708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值