河海大学第二十一届现场编程大赛-提高组 题解
如果觉得还有需要,你仍然可以连接我们的校园热点HHUC并登录10.11.12.75,在题库中能看到本次比赛的赛题,并可进行提交。
文章目录
题目A w
题目大意
判断所有消息的最后一个字母是否为w。
请注意:有的消息可能没有字母,请不要把用户名最后的w作为判断消息最后是否为w的依据。
大致思路
读入每行进行判断即可。
参考代码
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int main()
{
string s;
char Rec;
while (getline(cin, s))
{
int Pos = 0;
while (s[Pos] != ':' && Pos < s.length())Pos++;
Rec = 0;
for (int i = s.length() - 1; i > Pos; --i)
{
if (s[i] >= 'A' && s[i] <= 'Z' || s[i] >= 'a' && s[i] <= 'z')
{
if (s[i] != 'w')
{
printf("No\n");
return 0;
}
else break;
}
}
}
printf("Yes\n");
return 0;
}
题目B 一道难题
题目大意
现在给出一个正整数n,请找出最小的整数k使得集合{1, 2, 3, … ,n}中的取出任意k个数所组成子集T必然存在两个整数u,v∈T满足u是v的因子。
大致思路
观察数据范围,基本确定解题思路为找规律。
即可知选取超过一半的数字必然可使得有两个数字呈两倍关系。
参考代码
#include <cstdio>
#include <cstdlib>
using namespace std;
int T, n;
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
printf("%d ", (n + 1) / 2 + 1);
}
return 0;
}
题目C 数字迷宫
题目大意
寻找所有区域中长度大于4、不能被拓展、相邻格子差值递增为1的路径。
大致思路
首先排除搜索。题目中n、m数据量过大。观察题意,发现可以得到每个格子对先前的无后效性地推公式,即考虑使用动态规划。
Dp[n][m][k]表示在第n行第m列中的那个数作为找到路径的第k个格子存在的不同路径(k≥4时可以视作最大值k=4,因为所有计算出的值无论是大于4的任何值都符合题意了)。
依据地图中原有的值的大小进行排序,先进行小的值Dp的计算。
那么动态转移方程Dp[n][m][k]的值必然从其四周4个方格,并需要符合差值为1。取得Dp[n’][m’][k-1]。Dp[n][m][4]亦可从Dp[n’][m’][4]转移得到。
如果这个格子边上不存在比较其大于1的格子,则路径结束,对答案值进行统计。
请注意对答案取模。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int Modd = 1000000007;
int n, m;
int ans;
int Map[1010][1010];
struct node
{
int num, x, y;
}a[1000010];
int dp[1010][1010][5];
bool cmp(node x, node y)
{
return x.num < y.num;
}
const int Go[4][2] = { {0,1},{1,0},{-1,0},{0,-1} };
int main()
{
memset(dp, 0, sizeof(dp));
memset(Map, 0x7f, sizeof(Map));
ans = 0;
scanf("%d%d",&n,&m);
int num = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
scanf("%d", &Map[i][j]);
a[++num].x = i;
a[num].y = j;
a[num].num = Map[i][j];
}
sort(a + 1, a + 1 + num, cmp);
bool flag1, flag2;
for (int i = 1; i <= num; ++i)
{
flag1 = true;//标记当前格子是否没有后继格子可走
flag2 = true;//标记当前格子是否没有前置格子到达
for (int j = 0; j < 4; ++j)
{
if (Map[a[i].x][a[i].y] + 1 == Map[a[i].x + Go[j][0]][a[i].y + Go[j][1]])flag1 = false;
if (Map[a[i].x][a[i].y] - 1 == Map[a[i].x + Go[j][0]][a[i].y + Go[j][1]])
{
flag2 = false;
for (int k = 2; k <= 4; ++k)
dp[a[i].x][a[i].y][k] = (dp[a[i].x][a[i].y][k] + dp[a[i].x + Go[j][0]][a[i].y + Go[j][1]][k - 1]) % Modd;
dp[a[i].x][a[i].y][4] = (dp[a[i].x][a[i].y][4] + dp[a[i].x + Go[j][0]][a[i].y + Go[j][1]][4]) % Modd;
}
}
if (flag1)ans = (ans + dp[a[i].x][a[i].y][4]) % Modd;
if (flag2)dp[a[i].x][a[i].y][1] = 1;
}
printf("%d", ans);
return 0;
}
题目D 下围棋
题目大意
依据题目要求进行一系列的操作。
大致思路
依据题意进行模拟。该题为代码量题。可以适当采用并查集对围棋是否连块进行判断。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
int Go[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m;
char S[1010];
int Map[1010][1010];
bool used[1010][1010];
queue<int> Q;
struct Point
{
int x,y;
bool operator <(const Point yy)const
{
if(x==yy.x)return y<yy.y;
return x<yy.x;
}
bool operator ==(const Point yy)const
{
return (x==yy.x && y==yy.y);
}
};
struct node
{
int Fx,Fy;//Father
int G;
int Col;
set<Point> s;
}IF[1010][1010];
int xx,yy;
int Tot=0; Point jl[10];
inline int Change(char ch){if(ch=='.')return 0;if(ch=='b')return 1;if(ch=='w')return 2;return 3;}
inline char Change(int num){if(num==0)return '.';if(num==1)return 'b';if(num==2)return 'w';return ' ';}
inline void AddGas(int x,int y,int gx,int gy)
{
Point tmp;
tmp.x=gx; tmp.y=gy;
if(IF[x][y].s.count(tmp))return;
IF[x][y].s.insert(tmp);
++IF[x][y].G;
}
inline bool In(int x,int y)
{
if(x>n || x<1)return false;
if(y>n || y<1)return false;
return true;
}
inline void Search(int xx,int yy)
{
used[xx][yy]=true;
IF[xx][yy].Fx=xx; IF[xx][yy].Fy=yy; IF[xx][yy].Col=Map[xx][yy]; IF[xx][yy].G=0;
if(Map[xx][yy]==0)return;
Q.push(xx); Q.push(yy);
int x,y;
int tx,ty;
while(!Q.empty())
{
x=Q.front(); Q.pop(); y=Q.front(); Q.pop();
for(int i=0;i<4;++i)
{
tx=x+Go[i][0]; ty=y+Go[i][1];
if(Map[tx][ty]==Map[x][y] && !used[tx][ty])
{
Q.push(tx); Q.push(ty);
used[tx][ty]=true;IF[tx][ty].Fx=xx;IF[tx][ty].Fy=yy;IF[tx][ty].Col=Map[xx][yy];
}
if(Map[tx][ty]==0)AddGas(xx,yy,tx,ty);
}
}
}
inline Point GetFa(Point x)
{
//if((Point){IF[x.x][x.y].Fx,IF[x.x][x.y].Fy}==x)return x;
if(IF[x.x][x.y].Fx==x.x && IF[x.x][x.y].Fy==x.y)return x;
Point HH; HH.x=IF[x.x][x.y].Fx; HH.y=IF[x.x][x.y].Fy;
HH=GetFa(HH);
IF[x.x][x.y].Fx=HH.x; IF[x.x][x.y].Fy=HH.y;
return HH;
}
inline int Calc_Gas(int xx,int yy,int C)
{
int tot=0; Point Ss;
for(int i=0;i<4;++i)
if(In(xx+Go[i][0],yy+Go[i][1]))
tot+=(IF[xx+Go[i][0]][yy+Go[i][1]].Col==0);
Tot=0;
for(int i=0;i<4;++i)
if(IF[xx+Go[i][0]][yy+Go[i][1]].Col==C)
{
Ss.x=xx+Go[i][0]; Ss.y=yy+Go[i][1];
jl[++Tot]=GetFa(Ss);
tot+=IF[jl[Tot].x][jl[Tot].y].G-1;
}
return tot;
}
inline bool cmp(Point x,Point y)
{
return IF[x.x][x.y].G>IF[y.x][y.y].G;
}
inline void Link(int xx,int yy,int C)
{
Point Ss;
sort(jl+1,jl+1+Tot,cmp);
int x,y;
if(Tot)x=IF[jl[1].x][jl[1].y].Fx,y=IF[jl[1].x][jl[1].y].Fy;
else
{
x=xx,y=yy;
IF[xx][yy].G=0; IF[xx][yy].s.clear();
}
IF[xx][yy].Fx=x; IF[xx][yy].Fy=y; IF[xx][yy].Col=C;
for(int i=2;i<=Tot;++i)
{
if(x==jl[i].x && y==jl[i].y)continue;
IF[jl[i].x][jl[i].y].Fx=x;
IF[jl[i].x][jl[i].y].Fy=y;
while(!IF[jl[i].x][jl[i].y].s.empty())
{
Ss=*IF[jl[i].x][jl[i].y].s.begin();
IF[jl[i].x][jl[i].y].s.erase(IF[jl[i].x][jl[i].y].s.begin());
if(IF[x][y].s.count(Ss)==0)
{
IF[x][y].s.insert(Ss);
++IF[x][y].G;
}
}
}
for(int i=0;i<4;++i)
if(In(xx+Go[i][0],yy+Go[i][1]))
if(IF[xx+Go[i][0]][yy+Go[i][1]].Col==0)
{
Ss.x=xx+Go[i][0];
Ss.y=yy+Go[i][1];
if(IF[x][y].s.count(Ss)==0)
{
IF[x][y].s.insert(Ss);
++IF[x][y].G;
}
}
if(Tot)
{
--IF[x][y].G;
Ss.x=xx; Ss.y=yy;
IF[x][y].s.erase(Ss);
}
}
inline bool Find(int xx,int yy)
{
for(int i=0;i<4;++i)if(IF[xx+Go[i][0]][yy+Go[i][1]].Col==IF[xx][yy].Col)return true;
return false;
}
inline void AddNearbyGas(int xx,int yy)
{
Point Ss,emp;
emp.x=xx; emp.y=yy;
for(int i=0;i<4;++i)
{
Ss.x=xx+Go[i][0]; Ss.y=yy+Go[i][1];
Ss=GetFa(Ss);
if(IF[Ss.x][Ss.y].s.count(emp)==0)
{
IF[Ss.x][Ss.y].s.insert(emp);
++IF[Ss.x][Ss.y].G;
}
}
}
inline int Del(int xx,int yy,int C)
{
Point X,Ss; int ret=0;
X.x=xx; X.y=yy;
for(int i=0;i<4;++i)
{
if(IF[xx+Go[i][0]][yy+Go[i][1]].Col!=0 && IF[xx+Go[i][0]][yy+Go[i][1]].Col!=C)
{
Ss.x=xx+Go[i][0]; Ss.y=yy+Go[i][1];
Ss=GetFa(Ss);
if(IF[Ss.x][Ss.y].s.count(X))
{
--IF[Ss.x][Ss.y].G;
IF[Ss.x][Ss.y].s.erase(X);
if(IF[Ss.x][Ss.y].G==0)
{
if(Find(Ss.x,Ss.y))ret+=2;
else
{
ret+=1;
AddNearbyGas(Ss.x,Ss.y);
}
IF[Ss.x][Ss.y].Col=0;
}
}
}
}
return ret;
}
inline void Print()
{
Point Ss;
puts("");
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
Ss.x=i; Ss.y=j;
Ss=GetFa(Ss);
printf("%c",Change(IF[Ss.x][Ss.y].Col));
}
puts("");
}
}
int main()
{
memset(Map,-1,sizeof(Map));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%s",S+1);
for(int j=1;j<=n;++j)Map[i][j]=Change(S[j]);
}
memset(used,false,sizeof(used));
while(!Q.empty())Q.pop();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(!used[i][j])
Search(i,j);
bool B=false;
while(m--)
{
B^=true;
scanf("%d%d",&xx,&yy);
if(IF[xx][yy].Col!=0){printf("N");continue;}
int G=Calc_Gas(xx,yy,2-B);
if(G!=0)
{
printf("Y");
Link(xx,yy,2-B);
if(Del(xx,yy,2-B)>1)
{
Print();
printf("%sWin\n",B?"Black":"White");
return 0;
}
//Print();
continue;
}
if(int XX=Del(xx,yy,2-B))
{
printf("Y");
Link(xx,yy,2-B);
if(XX>1)
{
Print();
printf("%sWin\n",B?"Black":"White");
return 0;
}
//Print();
continue;
}
printf("N");
}
Print();
return 0;
}
题目E 玩游戏
题目大意
通过技能、移动进行刷野怪。计算t时间内的最大值。
大致思路
又是一道动态规划题。
动态方程4个状态分别表示,在哪个点,在第几秒,还有几次技能以及技能的CD。具体操作如代码所示。
参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n, m, t, k, b;
int a[55][55], e[55][55];
int f[65][55][6][6];
inline void checkMax(int &x, int y)
{
if (y > x) x = y;
}
inline void work()
{
memset(e, 0, sizeof e);
memset(a, 0, sizeof a);
scanf("%d %d %d %d %d", &n, &m, &t, &k, &b);
int x, y, z;
for (int i = 1; i <= m; ++i)
{
scanf("%d %d %d", &x, &y, &z);
e[x][y] = e[y][x] = z;
}
for (int i = 1; i <= k; ++i)
{
scanf("%d %d %d", &x, &y, &z);
a[x][y] += z;
}
memset(f, 0xbf, sizeof f);
for (int i = 1; i <= n; ++i)
f[1][i][0][b] = 0;
int cur, ans = 0;
for (int i = 1; i <= t; ++i)
for (int j = 1; j <= n; ++j)
for (int c = 0; c <= 5; ++c)
for (int d = 0; d <= b; ++d)
if (f[i][j][c][d] >= 0)
{
if (c == 0 && d > 0)
{
cur = f[i][j][c][d];
for (int z = 1; z <= n; ++z)
if (e[j][z] > 0)
cur += a[i][z];
checkMax(f[i][j][5][d - 1], cur);
}
cur = f[i][j][c][d] + a[i][j];
checkMax(ans, cur);
checkMax(f[i + 1][j][max(c - 1, 0)][d], cur);
for (int z = 1; z <= n; ++z)
if (e[j][z] > 0)
checkMax(f[i + e[j][z]][z][max(c - e[j][z], 0)][d], cur);
}
printf("%d\n", ans);
}
int main()
{
work();
return 0;
}
题目F 郊游
题目大意
通过一系列的计算还原值。
大致思路
数学题。使用快速幂计算即可。即 n n − n + 1 n^n-n+1 nn−n+1,不要忘记求余。
参考代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
long long pow_mod(long long a,long long n,long long m)
{
if (n==0) return 1;
if (n==1) return a%m;
long long x=pow_mod(a,n/2,m);
long long ans=(long long)x*x%m;
if (n%2) ans=ans*a%m;
return (long long)ans;
}
long long n,k;
long long w;
int main()
{
cin>>n>>k;
w=pow_mod(n,n,k)-n%k+1;
for (;w<0;w+=k);
cout<<w%k<<endl;
return 0;
}