实话实说,这次的难度要高很多,很多题目都是比赛的原题,虽然很多方法是和上次类似的,但可以说已经进阶了不少。不知道照这样下去,后面的题目会不会已经变成暑训的难度。。。
一道一道说一说吧
A
小模拟,只要按照题目要求就可以了,没有什么思维。
贴代码
# include <stdio.h>
const int MAX_N = 20;
int dx[4] = {1 , 0 , -1 , 0}, dy[4] = {0 , 1 , 0 , -1};
int f[MAX_N + 1][MAX_N + 1];
int N, M;
int main()
{
while(~scanf("%d %d", &N, &M))
{
if(M == 0 && N == 0)
break;
int i, j, k;
for(i = 0 ; i < N ; i++)
for(j = 0 ; j < M ; j++)
scanf("%d", &f[i][j]);
int d = -500, dh, dl;
for(i = 0 ; i < N ; i++)
{
for(j = 0; j < M ; j++)
{
int ps = (f[i][j] < 0) * 2 - 1 , sum = 0;
for(k = 0 ; k < 4 ; k++)
{
int nx = i + dx[k], ny = j + dy[k];
if(nx >= 0 && nx < N && ny >= 0 && ny < M)
sum += ps * f[nx][ny];
}
if(sum > d)
{
d = sum;
dh = i;
dl = j;
}
}
}
printf("%d %d %d\n", dh + 1, dl + 1, d);
}
return 0;
}
B
题目要求找出最大的一组连续数列,保证所有元素都不相同。一道变形的尺取法,只需要用一个set来存储连续数列的所有的不同元素即可。
贴代码
# include <stdio.h>
# include <set>
# include <algorithm>
using namespace std;
const int MAX_N = 1e6;
int A[MAX_N];
int T, N;
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &N);
int i;
for(i = 0 ; i < N ; i++)
scanf("%d", &A[i]);
int fro = 0, bak = 0, ans = 1;
set<int> s;
s.insert(A[0]);
for(i = 1 ; i < N ; i++)
{
if(s.find(A[i]) != s.end())
{
while(A[bak] != A[i])
s.erase(A[bak++]);
bak++;
fro++;
}
else
{
s.insert(A[i]);
fro++;
ans = max(fro - bak + 1 , ans);
}
}
printf("%d\n", ans);
}
return 0;
}
C
要求找到点集中所有的正方形,正好之前的训练赛做到一道类似的题目,只需要查询所有的点对,看看能不能组成一个正方形的对角点,然后把结果除二即可(因为每个正方形有两组对点),至于如何查找点对,这是一个数学问题,不是程序问题,这里就不多说了。
贴代码
# include <stdio.h>
# include <algorithm>
# include <set>
using namespace std;
typedef pair<int , int> P;
const int MAX_N = 1000;
set<P> s;
P pos[MAX_N];
int N;
inline int ab(int a)
{
return (a < 0) ? -a : a;
}
bool fin(P a , P b)
{
int dx = b.first - a.first, dy = b.second - a.second;
if((dx & 1) != (dy & 1))
return false;
int ia = (dx + dy) / 2, ib = (dy - dx) / 2;
return s.find(P(a.first + ia , a.second + ib)) != s.end() && s.find(P(b.first - ia , b.second - ib)) != s.end();
}
int main()
{
while(1)
{
scanf("%d", &N);
if(!N)
break;
s.clear();
int i, j;
for(i = 0 ; i < N ; i++)
{
scanf("%d %d", &pos[i].first, &pos[i].second);
s.insert(pos[i]);
}
int ans = 0;
for(i = 0 ; i < N ; i++)
{
for(j = i + 1 ; j < N ; j++)
{
if(fin(pos[i] , pos[j]))
{
ans++;
}
}
}
printf("%d\n", ans / 2);
}
return 0;
}
D
有点坑的一道题,反正瞎搞了一阵就搞好了,之前看别人说是贪心,于是就尝试了一下,每次的lr都尽量把这个数组往b上凑,于是就成功了。至于怎么凑,只要把他们的每一个元素标记为b中的位置,再进行一个排序即可。
# include <stdio.h>
# include <algorithm>
using namespace std;
const int MAX_N = 1000;
int A[MAX_N];
int C[MAX_N];
int T, N, M;
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &N, &M);
fill(C , C + N , -1);
int i, j;
for(i = 0 ; i < N ; i++)
scanf("%d", &A[i]);
int s;
for(i = 0 ; i < N ; i++)
{
scanf("%d", &s);
for(j = 0 ; j < N ; j++)
if(A[j] == s && C[j] == -1)
{
C[j] = i;
break;
}
}
int l, r;
for(i = 0 ; i < M ; i++)
{
scanf("%d %d", &l, &r);
sort(C + l - 1 , C + r);
}
for(i = 0 ; i < N ; i++)
{
if(C[i] != i)
{
puts("No");
break;
}
}
if(i == N)
puts("Yes");
}
return 0;
}
E
一道思维题,保证只能余一半一下,因为若可以余一半以上,那么那个模的数字必然小于余数,这是不成立的,并且所有余0至一半的都可以发现就是N-i类型的,于是成立。
贴代码
# include <stdio.h>
int main()
{
int N;
scanf("%d", &N);
while(N--)
{
int a;
scanf("%d", &a);
printf("%d\n", (a + 3) / 2);
}
return 0;
}
F
这道题非常麻烦,主要思想在于凑(L + 1),因为可以发现最密集的步数应该是每(L + 1)两步,所以凑一凑即可,这道题细节问题比较多,比如和之前的那个点还有后面的第二个点都要考虑,反正觉得是有点问题的。
贴代码
# include <stdio.h>
# include <algorithm>
using namespace std;
const int MAX_N = 2 * 1e5;
int A[MAX_N + 2];
int T, N, M, L;
int main()
{
scanf("%d", &T);
int tt = 0;
while(T--)
{
scanf("%d %d %d", &N, &M, &L);
int i;
for(i = 1 ; i <= N ; i++)
scanf("%d", &A[i]);
A[0] = 0;
A[N + 1] = M;
sort(A + 1 , A + N + 1);
int step = 0;
int k = L;
for(i = 0 ; i <= N ; i++)
{
int x = (A[i + 1] - A[i]) % (L + 1);
int y = (A[i + 1] - A[i]) / (L + 1);
if(k + x > L)
{
step += 2 * y + 1;
k = x;
}
else
{
step += 2 * y;
k += x;
}
}
printf("Case #%d: %d\n", ++tt, step);
}
return 0;
}
G
一道水题,直接写即可
贴代码
# include <stdio.h>
char a[2][20];
int main()
{
while(~scanf("%s %s", a[0], a[1]))
{
int n[2], j;
int i;
for(i = 0 ; i < 2 ; i++)
{
n[i] = 0;
for(j = 0 ; a[i][j] ; j++)
{
if(a[i][j] > 57 || a[i][j] < 48)
continue;
n[i] *= 10;
n[i] += a[i][j] - 48;
}
if(a[i][0] == '-')
n[i] *= -1;
}
printf("%d\n", n[0] + n[1]);
}
return 0;
}
H
首先要找一个平方数,还要保证这个数字没有超过2次的质因子,并且与x的绝对值最小,想了半天想不出什么好办法,就用了一个暴力查找,结果15ms过没说明这道题数据还是挺水的,思路是先找出不小于给出数的一个平方数(二分查找即可),然后往前后一个一个查找,每个数试一试是否成立,试到成立为止,然后再把前和后中较小的那个输出即可。
贴代码
# include <stdio.h>
# include <algorithm>
using namespace std;
typedef long long ll;
int T;
ll X;
ll ab(ll a)
{
return (a < 0) ? -a : a;
}
ll fin(ll x)
{
ll lb = 0, ub = min(x , (ll)1e9);
while(ub - lb > 1)
{
ll mid = (ub + lb) / 2;
if(mid * mid >= x)
ub = mid;
else
lb = mid;
}
return ub;
}
bool ok(ll k)
{
ll i;
for(i = 2 ; i * i <= k ; i++)
{
if(k % (i * i) == 0)
return false;
if(k % i == 0)
k /= i;
}
return true;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%lld", &X);
if(X < 4)
{
printf("%d\n", 4 - X);
continue;
}
ll ans;
ll n = fin(X);
int i = 0;
while(1)
{
if(ok(n + i))
{
ans = (n + i) * (n + i) - X;
break;
}
i++;
}
i = 1;
while(1)
{
if(ok(n - i))
{
ans = min(ans , X - (n - i) * (n - i));
break;
}
i++;
}
printf("%lld\n", ans);
}
return 0;
}
I
给出一组数,再给出一个范围问你这里面的数字有几个,没有任何对数组的操作,是一道简单的二分题,用一下二分函数即可。(另外建议记住精确这两个函数的作用和用法)
贴代码
# include <stdio.h>
# include <algorithm>
using namespace std;
const int MAX_N = 1e5;
int A[MAX_N];
int N, M, T;
int main()
{
int tt = 0;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &N, &M);
int i;
for(i = 0 ; i < N ; i++)
scanf("%d", &A[i]);
sort(A , A + N);
printf("Case %d:\n", ++tt);
int x, y;
for(i = 0 ; i < M ; i++)
{
scanf("%d %d", &x, &y);
printf("%d\n", upper_bound(A , A + N , y) - lower_bound(A , A + N , x));
}
}
return 0;
}
J
一道经典的贪心题(换汤不换药的题目做过n次了), 直接按照结束时间贪心地一个一个查找即可。
贴代码
# include <stdio.h>
# include <algorithm>
using namespace std;
typedef pair<int , int> P;
const int MAX_N = 100;
P tv[MAX_N];
int N;
int main()
{
while(~scanf("%d", &N))
{
if(!N)
break;
int i;
for(i = 0 ; i < N ; i++)
scanf("%d %d", &tv[i].second, &tv[i].first);
sort(tv , tv + N);
int t = 0, sum = 0;
i = 0;
while(i < N)
{
if(tv[i].second >= t)
{
sum++;
t = tv[i].first;
}
i++;
}
printf("%d\n", sum);
}
return 0;
}
K
一道大模拟,不过在经历过无数大模拟之后,我倒感觉还行,只要根据题目要求一点一点来即可,我建议能够模块化的东西一定要模块化,这种题写的越简洁越不容易出错,反正很多技巧不是一蹴而就的,需要大量的练习。这次的题目其实说的挺清晰的了,所以也没有遇到什么阻力就过了,甚至比做前面的题感觉还好 。
贴代码
# include <stdio.h>
# include <string.h>
const int MAX_N = 1e3 + 7;
int sx[3][4][4] = {{{0 , 0 , 1 , 1} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0}} , {{0 , 0 , 0 , 0} , {0 , 1 , 2 , 3} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0}} , {{0 , 0 , 1 , 2} , {0 , 0 , 0 , 1} , {0 , 1 , 2 , 2} , {0 , 1 , 1 , 1}}}, sy[3][4][4] = {{{0 , 1 , 0 , 1} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0}} , {{0 , 1 , 2 , 3} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0} , {0 , 0 , 0 , 0}} , {{0 , 1 , 0 , 0} , {0 , 1 , 2 , 2} , {1 , 1 , 0 , 1} , {0 , 0 , 1 , 2}}};
bool box[10][15];
int A[MAX_N];
char s[MAX_N];
int ans;
int T, N;
int tt;
bool chek(int x , int y , int z , int sp)
{
int i;
for(i = 0 ; i < 4 ; i++)
{
int nx = x + sx[sp][z][i], ny = y + sy[sp][z][i];
if(nx < 0 || nx >= 9 || ny < 0 || box[nx][ny])
return false;
}
return true;
}
bool sea(int n)
{
int i, j;
for(i = 0 ; i < 9 ; i++)
{
if(!box[i][n])
return false;
}
ans++;
for(i = 0 ; i < 9 ; i++)
{
for(j = n ; j < 11 ; j++)
{
box[i][j] = box[i][j + 1];
}
box[i][11] = 0;
}
return true;
}
void solve()
{
ans = 0;
memset(box , 0 , sizeof(box));
int tim = 0;
int shp, xu, px, py;
int i, j;
for(i = 0 ; i < N ; i++)
{
px = 3;
py = 8;
shp = A[i];
xu = 0;
while(1)
{
if(s[tim])
{
char c = s[tim++];
int cx = px, cy = py, cxu = xu;
if(c == 'a')
cx--;
else if(c == 'd')
cx++;
else if(c == 's')
cy--;
else if(c == 'w')
{
if(shp != 0)
cxu++;
if(cxu == 2 && shp == 1)
cxu = 0;
if(cxu == 4 && shp == 2)
cxu = 0;
}
if(chek(cx , cy , cxu , shp))
{
px = cx;
py = cy;
xu = cxu;
}
}
if(!chek(px , py - 1 , xu , shp))
{
for(j = 0 ; j < 4 ; j++)
box[px + sx[shp][xu][j]][py + sy[shp][xu][j]] = 1;
for(j = 0 ; j < 12 ; j++)
while(sea(j));
/*
int u, v;
for(u = 11 ; u >= 0 ; u--)
{
for(v = 0 ; v < 9 ; v++)
{
printf("%d ", box[v][u]);
}puts("");
}
puts("");
*/
break;
}
py--;
}
}
printf("Case %d: %d\n", tt++, ans);
}
int main()
{
scanf("%d", &T);
tt = 1;
while(T--)
{
scanf("%d", &N);
scanf("%s", s);
int i;
for(i = 0 ; i < N ; i++)
scanf("%d", &A[i]);
solve();
}
return 0;
}
L
好像在xdoj见过换汤不换药的题目,虽然我们不能快速正面计算出结果,但是如果给出一个预算,我们可以用n的复杂度验证是否成立,只要一个一个贪心地推即可,而且可以保证成立于不成立是单调的,所以直接用二分找出那个分界即可。
贴代码
# include <stdio.h>
const int MAX_N = 1e5;
int A[MAX_N];
int N, M;
bool ok(int n)
{
int sum = 1, res = n;
int i = 0;
while(i < N)
{
if(n < A[i])
return false;
if(res < A[i])
{
sum++;
res = n - A[i];
}
else
res -= A[i];
i++;
}
return sum <= M;
}
int main()
{
while(~scanf("%d %d", &N, &M))
{
int i;
for(i = 0 ; i < N ; i++)
scanf("%d", &A[i]);
int lb = 0, ub = 1e9;
while(ub - lb > 1)
{
int mid = (lb + ub) / 2;
if(ok(mid))
ub = mid;
else
lb = mid;
}
printf("%d\n", ub);
}
return 0;
}
M
思维题,因为可以任选a和b的和差所以可以发现一至N里面的元素有且只有被a和b的最大公约数整除的元素可以被选,而且不管怎么选最后都是能够选到全体的,所以只需要判断这些数的奇偶即可
贴代码
# include <stdio.h>
int N, T;
int A, B;
int gcd(int a , int b)
{
if(!b)
return a;
return gcd(b , a % b);
}
int main()
{
scanf("%d", &T);
int tt = 0;
while(T--)
{
scanf("%d %d %d", &N, &A, &B);
printf("Case #%d: ", ++tt);
if((N / gcd(A , B)) % 2 == 0)
puts("Iaka");
else
puts("Yuwgna");
}
return 0;
}