JZOJ 2019.05.25【NOIP普及组】模拟赛C组

2019.05.25【NOIP普及组】模拟赛C组

超链接为pdf题面,请先登录。

1384. 上学路线

本题正解是dp,没学过的,先做一下USACO的题目“数字金字塔”,会更好理解这题,
设 map[i][j] 为(1,1)到(i,j)的路径总数,设 bz[i][j] 为(i,j)是否有施工。
则 map[i][j]=map[i-1][j]+map[i][j-1],前提是 !bz[i][j]。
dp code:

#include
#include
#include
#include
using namespace std;
int map[20][20];
bool bz[20][20];
int a,b,n;
int main()
{
	int k,x,y;
	scanf("%d%d%d",&a,&b,&n);
	memset(map,0,sizeof(map));
	memset(bz,0,sizeof(bz));
	
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		bz[x][y]=true;
	}
	map[0][1]=true;
	for(int i=1;i<=a;i++)
		for(int j=1;j<=b;j++)
			if(!bz[i][j])
			    map[i][j]=map[i-1][j]+map[i][j-1];			    
	printf("%d\n",map[a][b]);
	return 0;
	//0ms 948kb
}

还能用搜索做,会略慢一些。
dfs code:

#include
#include
int n,m,ans=0;
int a[20][20];
int dx[2]={0,1};
int dy[2]={1,0};
struct nod{int x,y;}f[210000];
void dfs(int x,int y,int t)
{
	if(x==n&&y==m)
		ans++;
	else
		for(int i=0;i<=1;i++)
		{
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>=1&&yy>=1&&xx<=n&&yy<=m&&a[xx][yy]==0)
			{
				f[t].x=xx;f[t].y=yy;
				a[xx][yy]=1;
				dfs(xx,yy,t+1);
				a[xx][yy]=0;
			}
		}
}
int main()
{
	scanf("%d %d",&n,&m);
	int c;
	scanf("%d",&c);
	memset(a,0,sizeof(a));
	memset(f,0,sizeof(f));
	for(int i=1;i<=c;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		a[x][y]=1;
	}
	dfs(1,1,0);
	printf("%d",ans);
	return 0;
	//53ms 1.8mb
}

bfs code:

#include
//#include
//#include
using namespace std;
int queue[10000010][2];
int x,y;
int bx,by;
int n;
int map[17][17];
int zx[3]={0,1,0},zy[3]={0,0,1};
int bz[17][17];
int ans;
int main()
{
	for(int i=1;i<=17;i++)
		for(int j=1;j<=17;j++)
			map[i][j]=true;
	scanf("%d%d%d",&x,&y,&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&bx,&by);
		map[bx][by]=false;
	}
//	bfs();
	
	int head=0,tail=1;
	queue[1][0]=1,queue[1][1]=1;
	while(head<=tail)
	{
		head++;
//		bx=queue[head][0],by=queue[head][1];
		for(int i=1;i<=2;i++)
		{
			bx=queue[head][0]+zx[i],by=queue[head][1]+zy[i];
			if(map[bx][by]==true&&(1<=bx&&bx<=x)&&(1<=by&&by<=y))
			{
//				bz[queue[head][0]][queue[head][1]]=true;
				tail++;
				if(bx==x&&by==y)
					ans++;
				else
					queue[tail][0]=bx,queue[tail][1]=by;
//				system("cls");
//				for(int i=1;i<=x;i++)
//				{
//					for(int j=1;j<=y;j++)
//						if(i==bx&&j==by)
//							printf("*");
//						else
//							printf("%d",map[i][j]);
//					printf("\n");	
//				}
//				getch();
			}
		}
	}
	printf("%d\n",ans);
	return 0;
	//55ms 76.49MB
}

1385. 遗址

正解暴枚, 枚举任意两个点,画出第三和第四个点,判断是否存在。
eov code:

#include
#include
#define abs(a) ((a)>=0?(a):-(a))
#define max(a,b) ((a)>(b)?(a):(b))
long long int n,mx;
long long int x[5010],y[5010];
bool a[5010][5010];
using namespace std;
int main()
{
	int x1=0,x2=0,y1=0,y2=0,s=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		a[x[i]][y[i]]=true;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			x1=abs(y[i]-y[j])+x[i];
			y1=abs(x[i]-x[j])+y[i];
			x2=abs(y[i]-y[j])+x[j];
			y2=abs(x[i]-x[j])+y[j];
		 	if(x1<=5000&&x2<=5000&&y1<=5000&&y2<=5000)	
		 	{
		 		s=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
		 		if(a[x1][y1]&&a[x2][y2])
		 			mx=max(mx,s);
			}
//			if(s>mx)
//			{
		
//	            if(a[x1][y1]&&a[x2][y2])
		}
	}
	printf("%d\n",mx);
	return 0;
	//249 ms 24.21 MB
}

1386. 郁闷的记者

正解是拓扑排序,我查了一下,有一篇博客讲拓扑排序不错,不太会的能看一下。
看完之后,大家应该都对这题有思路了吧。
只要发现有两个入度为零的队伍的话,就得知有两个以上的胜负情况了。
TS code:

#include
#include
using namespace std;
struct node
{
	int num,pai;
}s[5010];
bool cmpai(node a,node b)
{
	return a.pai > b.pai || (a.pai == b.pai && a.num < b.num);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		s[i].num=i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		s[b].pai=min(s[a].pai-1,s[b].pai);
	}
	sort(s+1,s+n+1,cmpai);
	int ans=0;
	for (int i=1;i<=n;i++)
	{
		if(s[i].pai==s[i=1].pai)
			ans=1;
		printf("%d\n",s[i].num);
	}
	printf("%d\n",ans);
	return 0;
	//22ms 240kb
}

1387. 最轻的天平

这题考到了二叉树的运用,应该是这套比赛中最难的一题。
要想一个天平平衡,首先要使得左右天平两边平衡。假设左右两边的最轻重量 分别为W1,W2,设该天平左右两边的比例为L1:L2,要想使得该天平衡,可能左边要放大倍数 X,右边要放大倍数Y,则有以下关系式: W1XL1=W2L2Y;
即X/Y=(W2L2)/(W1L1),要想使天平重量最小,必须把X/Y化为最简分数,所以需要求出 W2L2和W1L1的最大公约数P,则X=W2L2 div P ,Y=W1L1 div P,整个天平的重量为 W1X+W2Y。
BT code:

#include
struct node
{
    long long int p,q,r,b;
}a[110];
int n,ans;
long long int f[110];
bool p[110];
int gcd(int a,int b)
{
    return (b==0)?(a):(gcd(b,a%b));
}
long long int dfs(int x)
{
    int l=a[x].r,r=a[x].b;
    long long int left=1,right=1;
    if(l)
		left=dfs(l);
    if(r)
		right=dfs(r);
    int ans=(left*a[x].p*right*a[x].q)/gcd(left*a[x].p,right*a[x].q);
    return ans/a[x].p+ans/a[x].q; 
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
	{
        scanf("%d%d%d%d",&a[i].p,&a[i].q,&a[i].r,&a[i].b);
        p[a[i].r]=true;
        p[a[i].b]=true;
    }
    for(int i=1;i<=n;++i)
        if(!p[i])
		{
            ans=i;
            break;
        }
    printf("%d\n",dfs(ans));
    return 0;
    //0ms 208kb
}

1351. 晚餐队列安排_扩展

这题是 JZOJ 1347的升级版。
如果是 1347 的话,暴力可以过,只用枚举1,2的分界线就行了。
但是这一题,当然不能枚举1,2,3……n的分界线,绝对超时。
一下是一个复杂度为O(nlogn)的思路
思路:根据给出的队列找出最长不下降子序列的长度,输出队列总长度-最长不下降子序列长度即可
f[i]表示以a[i]为结尾的最长不下降子序列长度;
g[i]表示长度为i的不下降子序列的最后一位的最小值是多少(具体实现方法同最长不下降子序列)
因为发现手打最长不下降子序列会超时,所以用upper_bound实现logn的查找。
DP code:

#include
#include
using namespace std;
int n,ma,f[50010],g[50010],a[50010];
int main()
{
//	freopen("diningb.in","r",stdin);
//	freopen("diningb.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    	scanf("%d",&a[i]);
    	f[i]=1;
    	g[i]=2e9;
    }
    for(int i=1;i<=n;i++)
    {
        int k=upper_bound(g+1,g+1+n,a[i])-g;//k是指针,所以最后别忘了减g(如果改为lower_bound则为上升子序列),k表示在数组中的位置
        f[i]=k;
        g[k]=a[i];
    }
    for(int i=1;i<=n;i++)
    	ma=max(ma,f[i]);
    printf("%d\n",n-ma);
    return 0;
    //11ms 788kb
}

这个code使用于 1347 和 1351。

祝大家coding愉快!

你可能感兴趣的:(比赛题解)