week 13

1.路径计数

有一个n×n�×�的网格,有些格子是可以通行的,有些格子是障碍。

一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。

由于答案很大,输出对109+7109+7取模的结果。

输入格式

第一行一个正整数n�。

接下来n�行,每行n�个正整数,11表示可以通行,00表示不能通行。

输出格式

一个整数,表示答案。

本题是一道基础递归题,记忆化搜索即可

#include <iostream>
using namespace std;
const int N = 1e9 + 7;
int a[105][105], b[105][105], n;
int dfs(int x, int y)//深度优先搜索
{
	int ans = 0;
	if (x == n && y == n)
		ans = 1;
	else
	{
		if (b[x][y] != 0)//记忆化搜索,节省时间
			ans = b[x][y];
		else
		{
			if (x < n && a[x + 1][y] == 1)
				ans += dfs(x + 1, y);
			if (y < n && a[x][y + 1] == 1)
				ans += dfs(x, y + 1);
			ans %= N;
			b[x][y] = ans;
		}
	}
	return ans;
}
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			cin >> a[i][j];
	cout << dfs(1, 1);
	return 0;

}

2

给定一个长度为 n 的数组 a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n并且 ap1<ap2<⋯<apm,使得ap1+ap2+⋯+apm最大。

输入格式

第一行一个数字 n。

接下来一行 n 个整数 a1,a2,…,an。

输出格式

一个数,表示答案。

本题是一道动态规划基础题,使用滚动数组即可解决

#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int a[N], n, f[N];
long long sum, ans;
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		f[i] = a[i];
	}

	for (int i = 1; i <= n; i++)//滚动数组
	{
		for (int j = 1; j <i; j++)
		{
			if (a[i] >= a[j])
				f[i] = max(f[i], a[i] + f[j]);
		}
	}
	int ans = f[1];
	for (int i = 1; i <= n; i++)
	{
		if (ans < f[i])
			ans = f[i];
	}
	cout << ans;
}

3

给定一个整数 n。你需要对它做 m 次操作。在一次操作中,你要将这个数的每一位 d� 替换成 d+1。比如,19121912 在进行一次操作后将变成 2102321023。

请求出整数 n 进行了 m 次操作后的长度。答案可能很大,输出对 109+7109+7 取模后的结果。

输入格式

​ 第一行一个整数 t,表示测试单元的个数。

​ 接下来 t 行,每行有两个整数 n和 m,表示最初的数字和进行多少次操作。

输出格式

​ 对于每个测试单元输出最终数字的长度,答案对 1e9+7 取模。

对于本题,我们可以发现经过每次加1后,数字i的数量即为加1前i-1的数量,(0的数量为加1前9的数量,1的数量为加1前9的数量和0的数量之和)发现了这个规律后,本题就比较简单了,需要注意的是,由于本体的输入量很大,需要用快读和printf输出防止超时

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 2e5 + 10, M = 1e9 + 7;
int a[10], p[N], q, t, x, y, m, i;
long long ans;
char b[10];

inline void read(int& a)
{
	int s = 0, w = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9')
	{
		if (ch == '-')
			w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	a = s * w;
}
int main()
{
	p[0] = 1;
	a[0] = 1;
	for (int i = 1; i <= N - 1; i++)//p[i]表示从0开始执行i次加1后数字的个数
	{
		p[i] = a[9] + p[i - 1];
		q = a[9];
		for (int i = 9; i>=2; i--)
		{
			a[i] = a[i - 1];
		}
		a[1] = a[0] + q;
		a[0] = q;
		a[1] %= M;
		p[i] %= M;
	}
	cin >> t;
	while (t--)
	{
		ans = 0;
		i = 0;
		scanf("%s", b);
		read(m);
		int x = strlen(b);
		for (i=0; i<x; i++)
		{
			y = b[i] - '0';
			ans += p[y + m];
		}
		ans %= M;
		printf("%d\n",ans);
	}

	return 0;
}

4

平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:

  1. 刚开始,派派已经位于某传送阵之上;
  2. 如果派派掌握一种魔法 (A,B),其中 A,B均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y)瞬间移动至 (X+A,Y+B);
  3. 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法

问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?

输入格式

第一行一个整数 N。

接下来一行 N 行,每行包含两个整数 Xi,Yi, 表示每个魔法阵的坐标。

输出格式

一个数,表示答案。

对于本题,我们可以发现,对于每一个距离(a,b),我们只需掌握a,

b除以它们最小公因数的魔法即可,此后如果有距离是该魔法的任意倍数,也可以该魔法解决,下面附上代码

#include <iostream>
#include <cmath>
#include <set>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 3e5;
typedef pair<int, int>ss;
set<ss>s;
int m, x[505], y[505], z, d, c, p, q, ans;
int main()
{

	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		cin >> x[i] >> y[i];
	}
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			if (j == i)
				continue;
			p = x[j] - x[i];
			q = y[j] - y[i];
			if (p == 0 || q == 0)
			{
				int u = 0;
				s.insert({ p,q });
			}
			else
			{
				
				if (abs(p) % abs(q) == 0 || abs(q) % abs(p) == 0)//求最简魔法
				{

					z = min(abs(p), abs(q));
					p /= z;
					q /= z;

				}
				s.insert({p,q});
			}
		}
	}
	ans = s.size();
	cout << ans;
	return 0;
}

5

对于一个长度为 n 的0101序列 a1,a2,…,an。

你可以执行以下操作任意多次:

  • 选择两个下标 1≤i,j≤n(i≠j)。

  • 记x=ai xor aj , y=ai or aj, 其中 xor 表示按位异或 , or 表示按位或。

  • 然后令 ai=x,aj=y或 ai=y,aj=x。

给定两个0101序列 s,t , 请你判断是否可以通过有限次(可以为00次)操作将序列 s变为 t。

输入格式

第一行一个整数 t, 表示数据的组数(1≤t≤103)。接下来 t 组数据:

每组第一行一个0101字符串 s(1≤|s|≤103),每组第二行一个0101字符串 t(1≤|t|≤103)。

注意:|s|可能不等于 |t|。

输出格式

如果可以通过有限次(可以为00次)操作将序列 s 变为 t , 输出 YES , 否则输出 NO

对于本题,我们可以发现,在两个字符串长度相等的前提下,如果一个字符串全为0而另一个不全为0,则第一个字符串无法转化为第二个字符串,知道了这点后,本题就十分简单了。

#include <iostream>
#include <string.h>
using namespace std;
char a[3005], b[3005];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int x = 0, y = 0,
		cin >> a >> b;
		if (strlen(a) == strlen(b))
		{
			for (int i = 0; i < strlen(a); i++)
			{
				if (a[i] == '0')
					x++;

				if (b[i] == '0')
					y++;

			}
			if (x == strlen(a) && y != strlen(a) || x != strlen(a) && y == strlen(a))
				cout << "NO" << '\n'; 
			else
				cout << "YES" << '\n';
		}
		else
			cout << "NO" << '\n';
}
	return 0;
}

6

我们称一个字符串为好字符串,指这个字符串中只包含01

现在有一个好字符串,求这个字符串中1恰好出现k次的子串有多少个。

输入格式

第一行给出一个数字k,表示子串中1的个数。

第二行给出好字符串。

输出格式

输出一个整数,表示好字符串中有多少个符合条件的子串

数据范围

0≤k≤106, |s|≤106

对于本题,如果k=0的话,由数学知识可得,对于n个连续的0,字串的个数是n*(n+1)/2;

如果k不等于0的话,我们可以用一下代码解决

for (int i = 1; i <= x; i++)//表示从i开始的字串需要含有1的个数
		{
			a[i] = k;
		}
int p=1;
		for (int i = 0; i < x; i++)
		{
			if (b[i] == '0')
			{
				for (int t = p; t <= x; t++)//如果a[t]为0的话,出现0可使字串的数量加1,如果a[t]大于0的话则无影响,因为该字串含1的数量还不够。
				{
					if (a[t] > 0)
						break;
					if (a[t] == 0)
					{
						ans++;
					}
				}
			}
			else
			{
				for (int j = p; j <= i + 1; j++)
				{
					a[j]--;
					if (a[j] == -1)//如果a[j]为-1的话,说明该字串含1的数量超出了k,可以放弃该子串了
						p = j + 1;
				}

			}
		}
		for (int i = 1; i <= x; i++)//在加上每个符合条件的字串
		{
			if (a[i] <= 0)
				ans++;
			else
				break;
		}

下面附上完整代码

#include <iostream>
#include <string.h>
using namespace std;
const long long N = 1e6 + 5;
long long a[N], k, ans, p = 1;
char b[N];
long long xx(long long x)
{
	long long t = x * (x + 1) / 2;
	return t;
}
int main()
{
	cin >> k;
	cin >> b;
	int x = strlen(b);
	if (k != 0)
	{
		for (int i = 1; i <= x; i++)
		{
			a[i] = k;
		}
		for (int i = 0; i < x; i++)
		{
			if (b[i] == '0')
			{
				for (int t = p; t <= x; t++)
				{
					if (a[t] > 0)
						break;
					if (a[t] == 0)
					{
						ans++;
					}
				}
			}
			else
			{
				for (int j = p; j <= i + 1; j++)
				{
					a[j]--;
					if (a[j] == -1)
						p = j + 1;
				}

			}
		}
		for (int i = 1; i <= x; i++)
		{
			if (a[i] <= 0)
				ans++;
			else
				break;
		}
	}
	else
	{
		int uu = 0;
		for (int i = 0; i < x; i++)
		{
			if (b[i] == '0')
			{
				uu++;
			}
			else
			{
				ans += xx(uu);
				uu = 0;
			}
		}
		ans += xx(uu);
	}
	cout << ans;
	return 0;
}

7

现在有一个栈,有 n 个元素,分别为 1,2,…,n。我们可以通过 push 和 pop 操作,将这 n 个元素依次放入栈中,然后从栈中弹出,依次把出栈的元素写下来得到的序列就是出栈序列。

比如 n=3,如果执行 push 1, push 2, pop, push 3, pop, pop,那么我们 pop 操作得到的元素依次是 2,3,1。也就是说出栈序列就是 2,3,1。

现在给定一个合法的出栈序列,请输出一个合法的由 push 和 pop 操作构成的操作序列。这里要求 push 操作一定是按 1,2,…,n的顺序。

输入格式

第一行一个整数 n。接下来一行 n 个整数,表示出栈序列。

输出格式

输出 2n行,每行一个 push 或 pop 操作,可以证明一个出栈序列对应的操作序列是唯一的。

本题是一道栈的基础题,了解栈的使用方法便可解决

#include <iostream>
#include <stack>
using namespace std;
stack <int>a;
int n, b[100005], j = 1;
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> b[i];
	}
	a.push(0);
	for (int i = 1; i <= n; i++)
	{
		cout << "push " << i<< '\n';
		a.push(i);
		while(a.top() == b[j])
		{
			if (a.top() == 0)
				break;
			a.pop();
			cout << "pop" << '\n';
			j++;
		}
	}
	while (a.top() != 0)
	{
		cout << "pop" << '\n';
		a.pop();
	}
	return 0;
}

8

您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:

  • 每行的黑色方块数与白色方块数相同。

  • 每列的黑色正方形数与白色方块数相同。

  • 没有行或列具有 33 个及以上相同颜色的连续正方形。

给定网格,确定它是否正确。

输入格式

第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。

接下来 n 行,每行包含一个长度为n的由字符BW组成的字符串,代表网格正方形的颜色。

输出格式

如果网格正确,请打印数字 1 在一行上。否则,请打印数字 0 在一行上。

本题是一道比较简单的题,直接上代码解释了、

#include <iostream>
using namespace std;
char a[25][25];
int b[25], c[25], n, x[25], y[25], z, t;
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> a[i][j];
			if (a[i][j] == 'W')//b[i]表示第i行w的数量,c[j]表示第j列w的数量
			{
				b[i]++;
				c[j]++;
			}
			if (i >= 2 && a[i][j] == a[i - 1][j])//x[j]表示某列具有相同颜色数目的正方形的数量减1
			{
				x[j]++;
				if (x[j] == 2)
					t = 1;
			}
			else
			{
				x[j] = 0;
			}
			if (j >= 2 && a[i][j] == a[i][j-1])//y[i]同理
			{
				y[i]++;
				if (y[i] == 2)
					t = 1;
			}
			else
			{
				y[i] = 0;
			}
		}
	}
		for (int i = 1; i <= n; i++)
		{
			if (b[i] != n / 2)
			{
				t = 1;
				break;
			}
			if (c[i] != n / 2)
			{
				t = 1;
				break;
			}
		}
		if (t == 0)
			cout << 1;
		else
			cout << 0;
		return 0;
}

9

Polycarp 有一个长度为 n的数组 a1,a2,...,an(n是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n) 使 ai减去 k。

​ 在 Polycarp 进行若干次操作后(可能 00 次),数组 a中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1−1。

输入格式

​ 第一行一个整数 t,表示测试单元的个数。

​ 接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,...,an。

输出格式

​ 对于每个测试单元输出单独一行一个整数 k (k≥1) —— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果 k能任意大的话。

直接上代码解释了

#include <iostream>
#include <cmath>
using namespace std;
int a[45], n, t, ans;
int divisor(int a, int b) //求最大公因数函数          
{
	int temp;                   
	if (a < b)                    
	{
		temp = a; a = b; b = temp;
	}
	while (b != 0)
	{
		temp = a % b;            
		a = b;
		b = temp;
	}
	return a;                  
}
int main()
{
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int i = 1; i <= n; i++)
			cin >> a[i];
		for (int i = 2; i <= n; i++)
		{
			ans = abs(a[i] - a[i-1]);
			if (ans != 0)
				break;
		}
		if (ans == 0)
			cout << -1 << '\n';
		else
		{
			for (int i = 1; i <= n; i++)
			{
				for (int j = i + 1; j <= n; j++)
				{
					if (a[j] == a[i])
						continue;
					int x = abs(a[j] - a[i]);
					ans = divisor(x, ans);//如果两数都不为0的话,求两数的最大公因数
					if (ans == 1)//1为最小答案,如果ans已经是1的话,可以直接结束
						break;
				}
			}
			cout << ans << '\n';
		}
	}
	return 0;
}

10

给定一个字符串,你可以删除多个(可以是 0) 相同 的字符,这样操作之后,你能否得到一个回文串?如果能,求最小化删除的个数。

输入格式

多组数据。

每一组数据包含两行,分别为字符串的长度 N,以及一个仅由小写字母组成的字符串 S。

输出格式

对于每一组数据,输出一行。

如果不可能得到一个回文串,输出 −1。反之则输出最小操作次数。

#include <iostream>
#include <string.h>
using namespace std;
const int N = 1e5 + 5;
char a[N], p;
int n, t, ans=1,j,i,q,x,y;
void pp(char p)
{
	while (y >= x + 1)
	{
		if (a[x] == a[y])
		{
			y--;
			x++;
		}
		else
		{
			if (a[x] == p)
			{
				ans++;

				x++;
			}
			else if (a[y] == p)
			{
				ans++;
				y--;
			}
			else
			{
				ans = -1;
				break;
			}
		}
	}
}
int main()
{
	cin >> t;
	while (t--)
	{
		p = ' ';
		ans = 0;
		q = 0;
		cin >> n;
		for (int i = 1; i <= n; i++)
			cin >> a[i];
	 j = n, i = 1;
		while (j >= i + 1)
		{
			if (a[j] == a[i])
			{
				j--;
				i++;
			}
			else
			{
					if (a[j - 1] == a[i])
					{
						 x = i, y = j;
						ans++;
						p = a[y];
						y--;
						pp(p);
						if (ans != -1)
						{
							q = ans;
						}
						ans = 0;
					}
					 if (a[i + 1] == a[j])
					{
						 x = i, y = j;
						ans++;
						p = a[x];
						x++;
						pp(p);
						if (q != 0)
						{
							if(ans!=-1)
							q = min(q, ans);
						}
						else
						{
							if (ans != -1)
							{
								q = ans;
							}
						}
						ans = 0;
					}
					 if (a[i] == a[i + 1])
					{
						 x = i; y = j;
						ans++;
						p = a[x];
						x++;
						pp(p);
						if (q != 0)
						{
							if(ans!=-1)
							q = min(q, ans);
						}
						else
						{
							if (ans != -1)
							{
								q = ans;
							}
						}
						ans = 0;
					}
					 if (a[j] == a[j - 1])
					{
						 x = i, y = j;
						ans++;
						p = a[y];
						y--;
						pp(p);
						if (q != 0)
						{
							if(ans!=-1)
							q = min(q, ans);
						}
						else
						{
							if (ans != -1)
							{
								q = ans;
							}
						}
		
					}
					 if (q == 0)
						 q = -1;
					 break;
				
			}
		}
		cout << q << '\n';
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值