OI 2017 吉大冬令营 Day 3

今天是Day3 虽然讲的知识点不难 但是考的不太好啊只有150,QAQ 说好的搜索成了暴力专题 出题人我问你矩阵乘法和搜索有关系嘛QAQ

Day3所讲知识点深度优先搜索、广度优先搜索、记忆化搜索、迭代加深搜索

D3T1 矩阵乘法

题目大意:

给定三个n*n的矩阵A,B,C,判断C是否等于A×B。(多组数据)

数据范围:

对于20%的数据,n=1,对于60%的数据,n<=100,对于100%的数据,1<=n<=1000
矩阵A和矩阵B中的元素为小于1000的非负整数。矩阵C中的元素在int范围内
考试的时候也没想出正解 就交了暴力 还附上了读入优化期望多拿点分 但最后还是60分嘛没有差距
先附上60分暴力代码

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n;
int a[1100][1100];
int b[1100][1100];
int c[1100][1100];
int read()
{
	int sum=0;
	char c;
	c=getchar();
	while(c==' '||c=='\n')
		c=getchar();
	while(c<='9'&&c>='0')
		sum=sum*10+(c-'0'),c=getchar();
	return sum;
}
void small_numberread()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&b[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&c[i][j]);
}
void big_numberread()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			b[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			c[i][j]=read();
}
int check()
{
	for(int i=n;i>=1;i--)
		for(int j=1;j<=n;j++)
		{
			int sum=0;
			for(int k=n;k>=1;k--)
				sum=sum+a[i][k]*b[k][j];
			if(sum!=c[i][j]) return 0;
		}
	return 1;
}
int main()
{
	scanf("%d",&t);
	while(t)
	{
		--t;
		memset(a,0,sizeof a);
		memset(b,0,sizeof b);
		memset(c,0,sizeof c);
		scanf("%d",&n);
		if(n<=100)small_numberread();
		else big_numberread();	
		if(check())printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

附上出题人正解:

矩阵乘法不满足交换律,即A×B不一定等于B×A。但矩阵乘法满足结合律。

  • 本题要求判断给定的矩阵是否满足
  • A × B=C。
  • 如果上式成立,在等号两边都左乘一个向量a,等号必然成立,即
  • a× (A×B)=a×C

根据结合律,即(a×A)×B=a×C

  • 因此,如果A × B=C,则(a×A)×B=a×C必然成立,反之则未必成立(必要条件)
  • 本题就是利用必要条件进行判断:随机一个n维向量a,检查是否有(a×A)×B=a×C。如果等号不成立,则原式必然不成立。

因为是向量与方阵相乘,每次判断的复杂度为O(n^2)。只要多判断几次,即可“几乎”判断出原式的正确性!

#include<cstdio>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 1010
#define MAXM 100010
#define INF 1000000000
int a[MAXN][MAXN],b[MAXN][MAXN],c[MAXN][MAXN],r[MAXN];
int ans1[MAXN],ans2[MAXN],ans3[MAXN];
int n;
void mul(int a[MAXN],int b[MAXN][MAXN],int s[MAXN])
{
	int tmp[1001];
	for(int j=1;j<=n;j++)
    {
    	tmp[j]=0;
    	for(int k=1;k<=n;k++)
    	   tmp[j]+=a[k]*b[k][j];
    }
    for(int i=1;i<=n;i++)s[i]=tmp[i];
}
bool jud()
{
	for(int i=1;i<=n;i++)
	    if(ans1[i]!=ans2[i])return 0;
	return 1;
}
int tmp;
int main()
{
	
	scanf("%d",&tmp);
	for(int i=1;i<=1000;i++)r[i]=rand();
	while(tmp--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=n;j++)
		        scanf("%d",&a[i][j]);
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=n;j++)
		        scanf("%d",&b[i][j]);
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=n;j++)
		        scanf("%d",&c[i][j]);
		mul(r,a,ans1);
		mul(ans1,b,ans1);
		mul(r,c,ans2);
		if(jud())printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

D3T2 交朋友

题目描述:

有n个OIers要交朋友 (n&1) 其中:第i个人和第j个人成为朋友要花费t[i][j]的时间,并且每个人只有唯一的朋友,问要让所有人都拥有朋友最少要花费多长时间?
数据范围:对于40%的数据,N<=6,对于100%的数据,N<=16

思路:

因为数据量只有16,所以我们就选择DFS
面对第i个人,他只可能有两种情况
1:他没有朋友 2:他与之前的人成为了朋友
面对情况1 我们就可以从他之后的人开始 寻找还没有朋友的人
面对情况2 我们直接跳过他 进入下一层DFS

考试其实想到了正解 但是少了一个else (或return)!!! 没有else的话从情况2的DFS出来后又进入到了选人阶段 就算不超时间 答案也是错的嘛QAQ

100 分->40分 QAQ

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,ans=0x7f7f7f7f;
bool use[50];
int a[50][50];
void dfs(int deep,int sum)
{
	if(deep>n)
	{
		ans=min(ans,sum);
		return ;
	}
	if(use[deep]) dfs(deep+1,sum);
	else
	{
		for(int i=deep+1;i<=n;i++)
			if(!use[i])
			{
				use[i]=true;
				dfs(deep+1,sum+a[deep][i]);
				use[i]=false;
			}
		return ;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	dfs(1,0);
	printf("%d",ans);
	return 0;
}

D3T3 物以类聚

题目大意:

有n个糖果,标号为1...n,每个糖果都有一个种类,m次询问
标号L到标号R之间共有种类为k的糖有多少块。

数据范围:

对于 50%的数据, n,m<=2000
对于 100%的数据, n,m<=100000,k在int范围内

思路:

正解是反离散种类 二分查找

发表评论

邮箱地址不会被公开。 必填项已用*标注