2.23(Virtual Judge)

文章涉及了动态规划方法求解GridIceFloor的状态转移问题,字符串处理优化的StrictlySuperior和Reversible,环形结构的Findit!,以及使用二分法的JumpingTakahashi2等问题,展示了不同IT技术在解决算法竞赛题目中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A.  Grid Ice Floor**

这道题要去考虑能访问到的格子(i,j)的状态,我们发现只有5种,即当我们在格子(i,j)时,此时的运动状态为:

  • 方向往上
  • 方向往下
  • 方向往左
  • 方向往右
  • 停下来

因此我们就设dp[i][j][0/1/2/3/4/5]表示到达格子(i,j),此时状态是上述5种中的一个,这个情况是否发生,转移就根据当前状态,枚举后继状态转移即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n, m;
	cin >> n >> m;
	vector<string> tu(n);
	for (auto& i : tu)
		cin >> i;
	vector<vector<array<int, 5>>> ok(n,
		vector<array<int, 5>>(m, array<int, 5>{}));
	queue<array<int, 3>> team;
	array<int, 4> dx{1, -1, 0, 0};
	array<int, 4> dy{0, 0, 1, -1};
	team.push({1, 1, 2});
	team.push({1, 1, 0});
	ok[1][1][0] = ok[1][1][2] = ok[1][1][4] = 1;
	auto stop = [&](int x, int y) { return tu[x][y] == '#'; };
	while (!team.empty())
	{
		auto [x, y, d] = team.front();
		team.pop();
		if (d == 4)
		{
			for (int i = 0; i < 4; ++i)
			{
				int nx = x + dx[i], ny = y + dy[i];
				if (!stop(nx, ny) && !ok[nx][ny][i])
				{
					team.push({nx, ny, i});
					ok[nx][ny][i] = 1;
				}
			}
			continue;
		}
		int nx = x + dx[d], ny = y + dy[d];
		if (stop(nx, ny))
		{
			if (!ok[x][y][4])
			{
				team.push({x, y, 4});
				ok[x][y][4] = 1;
			}
		} else
		{
			team.push({nx, ny, d});
			ok[nx][ny][d] = 1;
		}
	}
	int ans = 0;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < m; ++j)
		{
			bool access = false;
			for (auto& k : ok[i][j])
				access |= k;
			ans += access;
		}
	cout << ans << '\n';
	return 0;
}

B.  Strictly Superior/

这道题就是去枚举每一个可能,判断是否符合题意即可。然后用bitset去优化时间。

#include<stdio.h>
#include<bitset>
using namespace std;
bitset<110>a[100010];
int b[100010];
int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i=1;i<=n;i++)
	{
		int c;
		scanf("%d%d", &b[i], &c);
		for (int j=1;j<=c;j++)
		{
			int x;
			scanf("%d", &x);
			a[i][x] = 1;
		}
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			if(i!=j)
			{
				if (b[i]<=b[j])
				{
					bitset<110>t=a[i]&a[j];
					if (t==a[j]&&(b[i]<b[j]||t!=a[i]))
					{
						printf("Yes\n");
						return 0;
					}
				}
			}
		}
	}
	printf("No\n");
	return 0;
}

C.  Reversible /

这道题就是用set<string>去存储字符串,然后去记录去重后的字符串长度就行。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL P = 131;
char ch[200010];
set<ULL>st;
ULL get_hash(int n);
int main()
{
	int n;
	scanf("%d", &n);
	for (int i=1;i<=n;i++)
	{
		scanf("%s", ch+1);
		int len=strlen(ch+1);
		ULL x=get_hash(len);
		reverse(ch+1, ch+1+len);
		ULL y=get_hash(len);
		if(st.find(x)==st.end()&&st.find(y)==st.end())
			st.insert(x);
	}
	printf("%lld", st.size());
	return 0;
}
ULL get_hash(int n)
{
	ULL h=0;
	for (int i=1;i<=n;i++)
		h = h*P+ch[i];
	return h;
}

D.  Find it!**

这道题题意的构图方式实际是一棵或多棵基环内向树,其特点是从任意一个点出发,必定会跑到环内。

故而随便从一个点进行DFS,记录一下每个点是否被访问过,当一个点第二次访问时,它就是环上的点。然后从它遍历一下得到环上的点即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;
	cin >> n;
	vector<int> to(n);
	for (int i = 0; i < n; ++i)
	{
		int v;
		cin >> v;
		--v;
		to[i] = v;
	}
	int u = 0;
	vector<int> visit(n);
	for (u = 0; !visit[u]; u = to[u])
	{
		visit[u] = 1;
	}
	vector<int> ans{u};
	for (int i = to[u]; i != u; i = to[i])
	{
		ans.push_back(i);
	}
	cout << ans.size() << '\n';
	for (auto& i : ans)
		cout << i + 1 << ' ';
	cout << '\n';
	return 0;
}

E.  Vacation Together

这道题就是先用一个二维数组来存储所以字符串,然后按列去遍历这个二维数组,如果没有出现x就c++,然后用maxc去记录最大的c值,即为连续最多有几个o。

#include<stdio.h>
#include<math.h>
using namespace std;
int main()
{
	int n, d;
	scanf("%d%d", &n, &d);
	char a[n][d+1];
	for(int i=0;i<n;i++)
	{
		scanf("%s", a[i]);
	}
	int c=0, maxc=-1;
	int k;
	for(int j=0;j<d;j++)
	{
		k=0;
		for(int i=0;i<n;i++)
		{
			if(a[i][j]=='x')
				k=1;
		}
		if(k==0)
			c++;
		if(k==1)
			c=0;
		maxc=max(maxc, c);
	}
	printf("%d\n", maxc);
	return 0;
}

F.  Number Box**

这道题的思路就是:两个负数无论相差多选都可以相互抵消,一个0可以抵消任意多个任意距离的负数,所以去统计矩阵中负数出现的个数和标记0是否出现即可。奇数个负数时,使整个矩阵最小的数为唯一负数即可。

G.  Jumping Takahashi 2**

这道题我在网上看的思路就是二分答案,然后暴力建图 dfs 就行了。虽然就这短短一句话,但我感觉难度还是挺大的,对我来说。

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
	for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
	return x*f;
}
const int MN=205;
int x[MN],y[MN];
vector<int>G[MN];
int dis(int i,int j)
{
	return abs(x[i]-x[j])+abs(y[i]-y[j]);
}
int n,p[MN];
const int INF=5e9;
bool vis[MN];
void dfs(int u)
{
	vis[u]=1;
	for(int v:G[u])if(!vis[v])dfs(v);
}
bool chk(int v)
{
	for(int i=1;i<=n;i++)G[i].clear();
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==j)continue;
			if(dis(i,j)<=p[i]*v)G[i].push_back(j);
		}
	}
	for(int i=1;i<=n;i++)
	{
		memset(vis,0,sizeof(vis));
		dfs(i);bool pd=1;
		for(int k=1;k<=n;k++)if(!vis[k])pd=0;
		if(pd)return 1;
	}
	return 0;
}
signed main(void)
{
	n=read();
	for(int i=1;i<=n;i++)x[i]=read(),y[i]=read(),p[i]=read();
	int L=0,R=INF,ans=INF;
	while(L<=R)
	{
		int mid=(L+R)>>1;
		if(chk(mid))R=mid-1,ans=mid;
		else L=mid+1;
	}
	cout<<ans<<endl;
	return 0;
}

H.  When?

这道题没什么难度,因为就21和22两个时间,主要就是%02d用来输出个位数的时间。

#include<stdio.h>
int main()
{
	int k;
	scanf("%d", &k);
	if(k<60)
	{
		printf("21:%02d", k);
	}
	else
	{
		printf("22:%02d", k-60);
	}
	return 0;
}

I.  Rotation

这道题一开始我是想用string s去存储数据,然后在1的时候用s.insert()去把最后一个字符移到最前面,然后再读取目标字符,结果超时了。。。

然后改为用sx去记录到底移动了多少次,因为每移动原字符串长度次,那么就会又变为原字符串,故此时sx=sx%n。然后再去数目标字符到底在哪。

#include<stdio.h>
int main()
{
	int n, q;
	scanf("%d%d", &n, &q);
	char a[n+1];
	scanf("%s", a);
	long long sx=0;
	for(int i=0;i<q;i++)
	{
		int x1, x2;
		scanf("%d%d", &x1, &x2);
		if(x1==1)
		{
			sx+=x2;
		}
		if(x1==2)
		{
			if(sx/n>0)
			{
				sx=sx%n;
			}
			if(sx>=x2)
			{
				printf("%c\n", a[n-sx+x2-1]);
			}
			else
			{
				printf("%c\n", a[x2-sx-1]);
			}
		}
	}
	return 0;
}

J.  Trophy**

K.  Many Oranges

这道题就是按题目条件的要求去写就是。

#include <stdio.h>
int main()
{
	int a, b, w;
	scanf("%d%d%d", &a, &b, &w);
	w*=1000;
	int min, max;
	if(w%b==0)
	{
		min=w/b;
	}
	else
	{
		min=w/b+1;
	}
	max=w/a;
	if(min > max)
	{
		printf("UNSATISFIABLE\n");
	}
	else
	{
		printf("%d %d\n", min, max);
	}
	return 0;
}

L.  Alcoholic

这道题就是去判断什么是v*p/100>x,但要注意的就是用除非来进行判断可能出现误差,我就是因为这个没有过,呜呜难受。。。所以要用v*p>x*100。

#include<stdio.h>
int main()
{
	int n, x, sx=0;
	scanf("%d%d", &n, &x);
	for(int i=0;i<n;i++)
	{
		int v, p;
		scanf("%d%d", &v, &p);
		sx+=v*p;
		if(sx>x*100)
		{
			printf("%d", i+1);
			return 0;
		}
	}
	printf("-1");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值