大意: 给你一个 n × m n×m n×m 的 01矩阵,两个玩家一先一后把 0 的 a i j a_{ij} aij 变为 1,条件为第 i i i 行和第 j j j 列没有 1,当一名玩家无点可改,游戏结束
思路: 我们每改一个点,就会有 1 行和 1 列失效,那么,在这个 n × m n×m n×m 的 01矩阵 中,最多可以变 m i n ( n , m ) min(n, m) min(n,m) 个点,我们算出还有几个点可改,再判奇偶性即可
AC代码:
#include
using namespace std;
int tmp,n,m,a;
int l[55], r[55];
void solve()
{
memset(l, 0, sizeof(l));
memset(r, 0, sizeof(l));
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";
}
int main()
{
cin>>tmp;
while(tmp--)
solve();
return 0;
}
大意: 给你一个包含 n n n 个数的数组 a,和一 一对应的 type 数组 b(只含 0 和 1)你可以交换两个数 a i a_{i} ai 和 a j a_{j} aj,当且仅当 b i b_{i} bi 不等于 b j b_{j} bj,问是否经过任意次交换,使得数组 a 是非递减的
思路: 首先我们知道,如果 type 全为 0 或 1,这个数组是操作不了的,这时候判断他最初是不是非递减的,如果不是,就是NO;当 type 数组中有至少 1 个不同时,那它就是可以任意调换的
AC代码:
#include
using namespace std;
int tmp,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";
}
int main()
{
cin>>tmp;
while(tmp--)
solve();
return 0;
}
大意: 给你包含 n n n 个数的 a a a, b b b 数组,他们都是 [ 1 , n ] [1,n] [1,n] 的任意一个排列,你可以左移或右移任意数组任意次,问在所有情况中, a a a, b b b 数组能对上的位置最多有多少个。当 i = = j i==j i==j 且 a i = = b i a_{i}==b_{i} ai==bi,我们说这个位置能对上。左移一个数组表示在下方,右移相反。
思路: 我们确定数组 a a a、 b b b 每相同的数字对齐要移动多少格(定为第二个数组右移格数),相同的移动格数,最多的则为最终答案
AC代码:
#include
using namespace std;
int tmp,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) 中最大的数
}
int main()
{
solve();
return 0;
}
大意: 给你 n × m n×m n×m 的迷宫,包含如下几种 Cell
Empty — ‘.’
Wall — ‘#’
Good person — ‘G’
Bad person — ‘B’
保证出口的 Cell ( n , m ) (n, m) (n,m) 为Empty
我们可以在任意 Empty 的 Cell 上建立一个 Wall,使得这个 Cell 无法到达,B 和 G 只能上下左右移动,且不能移动到 Wall 的 Cell,但可以移动到 B 或 G 的 Cell,问我们能不能通过建墙,使得所有 B 不能到达出口,且所有 G 都能到达出口(允许没有 G)
思路: 先把 B 困住是比较容易的,只要把 B 的 4 个方向都建墙就行,建好后再从出口 bfs 一遍,把能走到的 Cell 标记一下,看看有没有哪个 G 的位置是没有被标记到的
AC代码:
#include
using namespace std;
#define pii pair
#define pb push_back
#define clr(a, b) memset((a), (b), sizeof(a))
int tmp,n,m,x,y;
char mp[55][55];
vector<pii>G, B;//存放 B、G的坐标
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);
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;
for (pii p : B)//遍历 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] = '#';
}
//我们在给 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";
}
int main()
{
cin>>tmp;
while(tmp--)
solve();
return 0;
}
大意: 给你 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 个数能让最后答案最大,比如 3,4,1,二进制为011,100, 001。三个数字全选,二进制里三个位置为 1 的数量都有 1 个,因此答案为 20 + + + 21 + + + 22 = = = 7
思路: 贪心算法,当 k = 0 、 1 、 2 、 3 k=0、1、2、3 k=0、1、2、3 时结果都是 1,但当 k = 4 k=4 k=4 时,选出的二进制必须有 2 个数的第 i i i 位位置为 1 才能 += 2 i 2^i 2i,所以最好选择 3 个数字( k = 3 k=3 k=3),而且是最优的 3 个数字,因此对所有的数暴力列举( n 3 n^3 n3)即可
AC代码:
#include
using namespace std;
#define ll long long
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;
}
int main()
{
solve();
return 0;
}
大意: 给你一个数组 a,每次只能选取一个数 K K K 交换前缀的 K K K 个数和后缀的 K K K 个数,问是否能变成数组 b
思路: 举个例子, 12345 1 2 3 4 5 12345,可以变成 52341 5 2 3 4 1 52341、 41352 4 1 3 5 2 41352、 21354 2 1 3 5 4 21354,多试几个样例发现对称的数经过交换后的位置依然是相对对称的,像这个例子中 1 和 5,2 和 4 的位置都是对称的,因此我们可以直接遍历 b 数组,如果 b 数组中存在对称的位置和 a 数组对称的位置的数字相同,那么直接标记表示该位置已经插入过,最后要特判一下 n 为奇数时最中间那个位置的数字是否相同
AC代码:
#include
using namespace std;
#define clr(a, b) memset((a), (b), sizeof(a))
typedef long long ll;
int a[1000],b[1000],vis[1000];
int main()
{
int t,n;
cin>>t;
while(t--){
cin>>n;
clr(vis,0);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
int l=1,r=n;bool flag=true;
while(l<r){
int f=0;
for(int i=1;i<=n;i++){ // a 数组第 i 个一个个和 b 数组对比,直到找到相同的数值
if(a[l]==b[i]&&a[r]==b[n-i+1]&&!vis[i]&&!vis[n-i+1]){
vis[i]=1,vis[n-i+1]=1,f=1;break;
}
}
if(!f){
flag=false;break;
}
l++,r--;
}
if(n&1&&a[n/2+1]!=b[n/2+1]) flag=false;
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}