P11001 题解
题目分析
这道题的题意比较容易理解,就是求出每一条对角线上相同的数字总共有多少对。
解题思路
暴力
最先想到的是暴力做法,即枚举
但是数据范围否定了这种做法。
对于
20\% 的评测用例,1 \leq n, m \leq 50 ;对于所有评测用例,
1 \leq n, m \leq 1000 ,1 \leq A_{i,j} \leq 1000 。
因此暴力做法会导致超时,但是能拿到
数学方法
第二个思路为利用数学方法求解。
如果有一条对角线
所以我们只需要记录下每条对角线上每个数字出现的次数,再利用上述的方法将所有结果累加起来,就能得到最终的答案。 (似乎也挺暴力的。)
补充一个小知识点:
求在
接下来有一个问题:如何表示每条对角线?
不难发现这样的规律:对于左上到右下的对角线(类似于 \ 这样的)上的所有数,行号与列号的差相等;而对于左下到右上的对角线(类似于 / 这样的)上的所有数,行号与列号的和相等。
根据这个规律我们可得出:如果一个数
分析完思路啦,下面是本题 AC 代码。
#include<iostream>
#include<limits.h>
using namespace std;
int A(int n,int m){ //用于求排列数
int res=1;
for(int i=n-m+1;i<=n;i++){
res*=i;
}
return res;
}
int n,m;
int a[1005][1005];
int bucket1[2005][1005]; //用于统计左上到右下的对角线
int bucket2[2005][1005]; //用于统计左下到右上的对角线
int main(){
cin>>n>>m;
int maxn=0,minn=INT_MAX; //maxn和minn用于计算循环范围
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
maxn=max(maxn,a[i][j]);
minn=min(minn,a[i][j]);
bucket1[i-j+1000][a[i][j]]++; //记得+1000来保证下标非负
bucket2[i+j][a[i][j]]++;
}
}
int sum=0;
for(int i=1;i<=2001;i++){ //枚举每一条对角线
for(int j=minn;j<=maxn;j++){ //枚举每个可能出现的数
sum+=A(bucket1[i][j],2)+A(bucket2[i][j],2); //累加排列数
}
}
cout<<sum;
return 0;
}