传送门
给你一个 n × m n×m n×m的01矩阵,两个玩家一先一后把为0的 a a a i j ij ij变为1,条件为第 i i i行和第 j j j列没有1,当一名玩家无点可改,lose。我们每改一个点,就会有1行和1列失效。显而易见,在这个 n × m n×m n×m的01矩阵中,最多可以变 m i n ( n , m ) min(n, m) min(n,m)个点。
因为在最初给的01矩阵中,也可能有1,我们要先算出还有几个点可改,再判奇偶性即可
#define clr(a, b) memset((a), (b), sizeof(a))
int n, m, a;
int l[55], r[55];
void solve()
{
clr(l, 0);//有t个case,记得初始化
clr(r, 0);
cin >> n >> m;
int L = n, R = m;//记录还有几行几列是没有1的
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
cin >> a;
if (a == 1)
{
if (l[i] == 0)L--;
if (r[j] == 0)R--;
l[i] = r[j] = 1;
}
}
if (min(L, R) % 2 == 1)cout << "Ashish\n";
else cout << "Vivek\n";
}
给你一个包含 n n n个数的数组 a [ ] a[] a[],和一 一对应的type数组 b [ ] b[] b[](只有0,1)。你可以交换两个数 a a a i i i, a a a j j j当且仅当 b b b i i i不等于 b b b j j j。问能否经过任意次交换,使得数组 a a a是非递减的。
首先我们知道,如果type全为0或1,这个数组是操作不了的,这时候判断他最初是不是非递减的,如果不是,就是NO。当type数组中有至少1个不同时,他是可以任意调换的:我们假设要交换相同type的 a a a i i i与 a a a j j j,我们找到与他们type不同的 a a a k k k,然后以 a a a k k k为中介, s w a p ( swap( swap( a a a i i i, a a a k k k ) ) ), s w a p ( swap( swap( a a a j j j, a a a k k k ) ) ), s w a p ( swap( swap( a a a i i i, a a a k k k ) ) ),这样就交换成功了。因此只要存在至少1个type是与其他不同的,这个数组就可以任意调换(可以自己举个例子,就清楚了),要变成非递减的也不是问题。
int n, m, x, y;
int a[550], b[550];
void solve()
{
int z = 0, o = 0;
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i <= n; i++)
{
cin >> b[i];
if (b[i] == 1)o = 1;
if (b[i] == 0)z = 1;
}
bool f = 0;
for (int i = 1; i < n; i++)
if (a[i] > a[i + 1])f = 1;
if (f && (z + o != 2))cout << "NO\n";//当type只有0或只有1,且原数组不是非递减的,输出NO
else cout << "YES\n";
}
给你包含 n n n个数的 a a a, b b b数组,他们都是 [ 1 , n ] [1,n] [1,n]的任意一个排列,你可以左移或右移任意数组任意位数一次,问在所有情况中, a a a, b b b数组能对上的位置最多有多少个。当 i i i = = == == j j j且 a a a i i i = = == == b b b i i i,我们说这个位置能对上。左移一个数组表示在下方,右移相反。
这道题,可能很多人最开始会想到kmp,hash,或者最长公共子序列,是的这些我都想过。 在真正了解了题意后,我们发现,找到某个数字 x x x在 a a a, b b b数组中的位置,他们的下标差( p o s a [ x ] − p o s b [ x ] posa[x]-posb[x] posa[x]−posb[x])即为:要使这个数字对上,需要左移或右移数组 b b b(这里我们固定 a a a数组,只移动 b b b数组,当然反着来也行)几个位置。在这里我们把右移当作正方向(下标差为正),左移当作负方向(下标差为负),如果下标差为2,表示右移数组 b b b,数字 x x x能对上,也表示当移动2位时,能对上的数字个数加1。我们不妨用num数组记录移动位置数不同时,能对上的数字个数。由于数组没有负的下标,且左移 x x x位等价于右移 n − x n-x n−x位,所以把左移 x x x位的 n u m [ − x ] num[-x] num[−x]表示为 n u m [ n − x ] num[n-x] num[n−x]。可能我的表达不够清晰,看下面的简介代码就知道了。
int n, m;
int a[200005], b[200005];
int posa[200005], posb[200005];//记录每个数字的位置
int num[200005];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
posa[a[i]] = i;
}
for (int i = 1; i <= n; i++)
{
cin >> b[i];
posb[b[i]] = i;
}
for (int i = 1; i <= n; i++)
{
int dif = posa[i] - posb[i];
if (dif < 0)dif += n;//小于0表示这里要左移,下标+n,表示为右移
num[dif]++;
}
//num[0]表示不移动时能对上的数字个数,移动n次就是没有移动,可以不用加入比较
cout << *max_element(num, num + n) << endl;//找到num数组[0,n)中最大的数
}
给你 n × m n×m n×m的迷宫,包含如下几种cell。
Empty — ‘.’
Wall — ‘#’
Good person — ‘G’
Bad person — ‘B’
保证出口的cell(n, m)为Empty。
我们可以在任意Empty的cell上建立一个Wall,使得这个cell无法到达。B和G只能上下左右移动,且不能移动到Wall的cell,但可以移动到B或G的cell。问我们能不能通过建墙,使得所有B不能到达出口,且所有G都能到达出口。先把B困住是比较容易的,只要把B的4个方向都建墙就行。建好后再从出口bfs一遍,把能走到的cell标记一下,看看有没有哪个G的位置是没有被标记到的。有很多细节,看代码。
#define pii pair
#define pb push_back
#define clr(a, b) memset((a), (b), sizeof(a))
int n, m, x, y;
char mp[55][55];
vector<pii>G, B;//存放BG的坐标
bool vis[55][55];//标记bfs过后能走到的位置
int dir[4][2] = { {1, 0},{-1, 0},{0,1},{0,-1} };//bfs四个方向
void bfs(int x, int y)//最简单的bfs模板
{
queue<pii>q;
q.push(pii(x, y));
vis[x][y] = 1;
while (q.size())
{
pii tmp = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int nx = tmp.first + dir[i][0];
int ny = tmp.second + dir[i][1];
if (nx > 0 && nx <= n && ny>0 && ny <= m && !vis[nx][ny] && mp[nx][ny] != '#')
{
vis[nx][ny] = 1;
q.push(pii(nx, ny));
}
}
}
}
void solve()
{
G.clear();
B.clear();
clr(mp, ' ');
clr(vis, 0);//mp也要初始化,我们在给B周围建墙的时候,如果B是在mp边界的,会被前面没有清空的mp影响
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
cin >> mp[i][j];
if (mp[i][j] == 'B')B.pb(pii(i, j));
else if (mp[i][j] == 'G')G.pb(pii(i, j));
}
bool f = 0;
//B的位置也要用数组存起来是因为,如果你是去遍历mp,找到B然后建墙,就会把B周围的B用Wall覆盖,明显是错的
for (pii p : B)
{
int i = p.first, j = p.second;
//如果B旁边就是G,那么只要存在G,且G能到出口,B也能,所有答案一定是NO
if (mp[i - 1][j] == 'G' || mp[i + 1][j] == 'G' || mp[i][j + 1] == 'G' || mp[i][j - 1] == 'G')
{
f = 1;
break;
}
if (mp[i - 1][j] == '.')mp[i - 1][j] = '#';
if (mp[i + 1][j] == '.')mp[i + 1][j] = '#';
if (mp[i][j + 1] == '.')mp[i][j + 1] = '#';
if (mp[i][j - 1] == '.')mp[i][j - 1] = '#';//只有Empty的cell才可以建墙
}
//我们在给B建墙的时候,可能会在出口处也建了墙,那么如果迷宫里面有G,是肯定出不来的
if (f || (G.size() > 0 && mp[n][m] == '#'))
{
cout << "NO\n";
return;
}
if (mp[n][m] != '#')bfs(n, m);//如果出口没有被堵住,就bfs
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
//B在被标记过的位置,表示可以逃出去
if (vis[i][j] && mp[i][j] == 'B')
{
cout << "NO\n";
return;
}
}
for (pii p : G)
{
//只要有一个G不在被标记的位置,答案为NO
if (vis[p.first][p.second] != 1)
{
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
题意是,给你 n n n个数,要你选出 k k k个数,先把他们转为2进制,对于二进制的第 i i i位,如果你选的 k k k个数里至少有 m a x ( 1 , k − 2 ) max(1, k-2) max(1,k−2)个数字的二进制的第 i i i位是1,答案就+= 2 i 2^i 2i。求怎么取 k k k个数能让最后答案为 Σ 2 i \Sigma2^i Σ2i最大。比如3,4,1,二进制为011,100, 001。三个数字全选,二进制里三个位置的1的数量都是至少有 m a x ( 1 , 3 − 2 ) max(1, 3-2) max(1,3−2)个,因此答案为 2 0 + 2 1 + 2 2 = 7 2^0+2^1+2^2=7 20+21+22=7。
我们观察 m a x ( 1 , k − 2 ) max(1, k-2) max(1,k−2),发现当 k = 0 , 1 , 2 , 3 k=0,1,2,3 k=0,1,2,3时结果都是1。所以我们可以至少选3个数(贪心)。现在我们来比较一下 k k k取3和 k k k取4,对于答案有什么影响。当k=3,我们选三个数字,假设有x个位置的1的数量是符合要求的(数字1的数量要求为1)。现在我们取k=4,即在这3个数基础上再选一个数,这时候数字1的数量要求就是2了,那么你选的前3个数的x个为1的位置,第4个数对应的位置都要为1,且第4个数还要有除了这x个位置外的位置要为1,答案才能增加,否则,选的越多,答案可能就会越小。显而易见,当我们取了最优的3个数之后,再取一个数,都可能会使答案变小。
比如1,3,8,12,我们取最优的三个数:1,3,12,这时候有4个位置符合要求,如果现在你还要选8,符合要求的位置数就会降为1。
我们就可以暴力n^3取最优的三个数,就可以得到答案。
1的数量越多,或者1的位置越靠前都不一定是最优的,可以很容易举出反例。
int n;
ll a[505];
ll ans = 0;
void solve()
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
// |这个竖杠表示当两个都为0是,结果才为0
ans = max(ans, a[i] | a[j] | a[k]);
}
}
}
cout << ans << endl;
}
感觉对cf的比赛有点上瘾了,难道这就是人菜瘾大吗QAQ…