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();
}