第二周集训

第一题:路径计数

评析:dp题,类似数字三角形,加个判断,如果是0,直接将f[i][j]数组置0,否则正常走路就可以

#include<bits/stdc++.h>
using namespace std;
int f[105][105],a[105][105],n,ans;
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++) 
			cin>>a[i][j];
	//f[0][1]=1;
	int x=1e9+7;
	f[0][1]=1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			if (a[i][j]==0) f[i][j]=0;
			else f[i][j]=(f[i-1][j]+f[i][j-1]) % x;
		}	
	cout<<f[n][n];
}

第二题:最大和上升子序列

评析:还是dp,两个最基础模型最长上升子序列和最大子段和的结合体

#include<bits/stdc++.h>
using namespace std;
int ans,n,f[1005],a[1005];
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++)
		for (int j=i;j>=1;j--)
		{
			if (a[i]>=a[j])
			f[i]=max(f[j]+a[i],f[i]);
		}
	for (int i=1;i<=n;i++) ans=max(ans,f[i]);
	cout<<ans;
}

第三题:加一

注意输入输出用scanf,printf

评析:比较难的一个dp,我们设定状态数组f[N] [10],N是m的上限:2*10^5,10是0~9,f[i] [j]的意思是:原本是j的数位经过i次操作后变成了f[i] [j]位。比如f[1] [9],就是原本这个位置上是9,经过了1次操作后,变成了10,即两位数。

状态转移方程有两种情况,j=0~8是:f[i] [j-1]=f[i-1] [j]; j=9时是f[i] [9]=(f[i-1] [0]+f[i-1] [1])%MOD。因为9加完后是10,位数就是1和0加起来。其余则和+1前的数一样。最后我们以字符串形式读入数字,以数字形式读入操作次数m,然后根据数字每位上的数,将f[m] [j]累加起来即可,注意要取模

#include<bits/stdc++.h>
using namespace std; 
const int MOD = 1e9 + 7;
long long f[200060][10];
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 0; i <= 9; i++)f[0][i] = 1;
	for (int i = 1; i <= 200010; i++)
	{
		for (int j = 1; j <= 9; j++)f[i][j - 1] = f[i-1][j];
		f[i][9] = (f[i-1][1] + f[i-1][0]) % MOD;
	}
	while (n--)
	{
		char str[20];
		int m, res = 0;
		scanf("%s %d", &str, &m);
		int len = strlen(str);
		for (int i = 0; i < len; i++)
		{
			res += f[m][str[i] - '0'];
			res %= MOD;
		}
		printf("%d\n",res);
	}
	return 0;
}

第四题:跳跳

评析:最大公约数,我们发现对于每一次魔法所需要的距离大小每一次都要尽可能短,因为每次移动魔法可以用很多次嘛,所以我们先预处理得到每一步的距离差,接着都除以两两间的最大公约数,注意要加绝对值,因为会出现负值,接着就是排序,比较,输出了

#include<bits/stdc++.h>
using namespace std;
int sum,n,a[250000],b[250000],k[250000],x[500],y[500];
struct point 
{
	int x;
	int y;
}c[250000];
bool cmp(point a,point b)
{
	return a.x<=b.x;
}
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++) cin>>x[i]>>y[i];
	int tot=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			if (i!=j) 
			{
				tot++;
				a[tot]=x[i]-x[j];
				b[tot]=y[i]-y[j];
				k[tot]=abs(__gcd(a[tot],b[tot]));
				c[tot].x=a[tot]/k[tot];
				c[tot].y=b[tot]/k[tot];
				//cout<<a[tot]<<" "<<b[tot]<<" "<<k[tot]<<" "<<c[tot].x<<" "<<c[tot].y<<endl;
			}
		}
	}
	sort(c+1,c+tot+1,cmp);
	for (int i=1;i<=tot-1;i++)
	{
		if ((c[i].x!=c[i+1].x && c[i].y!=c[i+1].y) || (c[i].x==c[i+1].x && c[i].y!=c[i+1].y) || (c[i].x!=c[i+1].x && c[i].y==c[i+1].y)) sum++;
		//if (c[i].x==c[i+1].x && c[i].y==c[i+1].y) sum--; 
	}
	cout<<sum+1;
}

第五题:异和异或

评析:一个思考题吧,多在纸上画一画,接可以看出,其实除全零外只有两种操作,还是可逆的,那么很明显的我们最后可以将任意带有1的字符串转化为1111……,除了全0,那么不就转换成了判断一个字符串里面有没有1的思路了吗,当然记得判断一下长度

#include<bits/stdc++.h>
using namespace std;
int f1,f2,len1,len2,n;
string s,t;
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>s>>t;
		int len1=s.length();
		int len2=t.length();
		if (len1 != len2) 
		{
			cout<<"NO"<<endl;
			continue;
		}
		else 
		{
			for (int j=0;j<len1;j++) 
				if (s[j]=='1') f1=1;
			for (int j=0;j<len2;j++) 
				if (t[j]=='1') f2=1;	
			if (f1==f2) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
			f1=0;
			f2=0;
			continue;
		}
	}
	return 0;
 } 

第六题:01序列

评析:前缀和

暴力做法可以求出所有的前缀和sum[i],只要sum[i]-sum[j]=k,[j+1,i]就是符合的子串区间
但这样统计太慢,其实到i点的sum[i]只要知道前面有多少sum[j]=sum[i]-k,而前缀和sum是单调不下降的
所以只要统计含1数量为sum[i]-k的前缀和数量有多少,这些j肯定是连续的,这些j的数量就是使得[j+1,i]内含有k个1的数量
用d[i]记录前缀和为i的数量,当前缀和sum>=k时,d[sum-k]的数量就可以累加到答案

记得判断一下全0 的情况

#include<bits/stdc++.h>
using namespace std;
long long k,ans,n,d[1000005],sum,f;
char s[1000005];
int main()
{
	d[0]=1;
	cin>>k;
	scanf("%s",s+1);
	n=strlen(s+1);
	if (k==0)
	{
		for (int i=1;i<=n;i++) 
		{
			if (s[i]=='1') f=1;
		}
		if (f==0) 
		{
			cout<<(n+1)*n/2;
			return 0;
		}
	}
	for (int i=1;i<=n;i++)
		{
			sum+=s[i]-'0';
			if (sum>=k) ans+=d[sum-k];
			d[sum]++;
		}
		cout<<ans<<endl;
 } 

第七题:出栈序列判断

评析:栈,不断判断该数是不是栈顶元素,还没入栈就push,再pop

#include<bits/stdc++.h>
using namespace std;
int n,x,l,top,s[100005];
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if (s[top]!=x)
		{
			for (int j=l+1;j<=x;j++)
			{
				printf("push %d\n",j);
				s[++top]=j;
			}
			l=x;
		}
		printf("pop\n");
		top--;
	} 
} 

第八题:序列维护

评析:stl基本操作,但我忘了,还是得多复习

#include<bits/stdc++.h>
using namespace std;
vector<int>v;
int n,x,y;
string s;
int main()
{
	cin>>n;
	for (int i=0;i<n;i++)
	{
		cin>>s;
		if (s[0]=='i') 
		{
			cin>>x>>y;
			v.insert(v.begin()+x,y);
		}
		else if (s[0]=='d')
		{
			cin>>x;
			v.erase(v.begin()+x-1);
		}
		else 
		{
			cin>>x;
			cout<<v[x-1]<<endl;
		}
	}
}

第九题:网格判断

评析:模拟题,分开判断行列W,B的个数,然后判断不能连续三个同种颜色就🆗了

#include<bits/stdc++.h>
using namespace std;
char ch[30][30];
int n,f=1,sum1,sum2;
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			cin>>ch[i][j];
		}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			if (ch[i][j]==ch[i][j-1] && ch[i][j]==ch[i][j+1]) 
			{
				f=0;
				cout<<f;
				return 0;
			}
			if (ch[i][j]=='W') sum1++;
			else sum2++;
			//cout<<sum1<<" "<<sum2<<endl;
		}
		if (sum1==sum2) f=1;
		else 
		{
			f=0;
			cout<<f;
			return 0;
		}
		sum1=0;
		sum2=0;
	}	
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			if (ch[j][i]==ch[j-1][i] && ch[j][i]==ch[j+1][i]) 
			{
				f=0;
				cout<<f;
				return 0;
			}
			if (ch[j][i]=='W') sum1++;
			else sum2++;
		}
		if (sum1==sum2) f=1;
		else 
		{
			f=0;
			cout<<f;
			return 0;
		}
		sum1=0;
		sum2=0;
	}
	cout<<f;
}

第十题:整齐的数组

评析:又是最大公约数,大概操作和跳跳差不多

#include<bits/stdc++.h>
using namespace std;
int t,n,a[105],b[105],x,c[105];
int main()
{
	cin>>t;
	int ans=1e7;
	for (int i=1;i<=t;i++)
	{
		cin>>n;
		for (int j=1;j<=n;j++) cin>>a[j];
		sort(a+1,a+n+1); 
		for (int j=1;j<n;j++)
		{
			b[j]=abs(a[j]-a[j+1]);	
			//if (b[i]==0) b[i]=1e7;
		}
		sort(b+1,b+n);
		for (int j=1;j<n;j++)
		{
			//cout<<b[j]<<" ";
			//if (b[j]!=0 && b[j+1]!=0) 
			//ans=min(__gcd(b[j],b[j+1]),ans);
			if (b[j]!=0) c[++x]=b[j];
		}
		//cout<<x<<endl;
		if (x==0)
		{
			cout<<"-1"<<endl;
			x=0;
			continue;
		}
		if (x>=2) 
		{
			for (int j=1;j<x;j++) ans=min(__gcd(c[j],c[j+1]),ans);
		}
		else 
		{
			cout<<c[x]<<endl;
			x=0;
			continue;
		}
		cout<<ans<<endl;
		ans=1e7;
		x=0;
	}
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值