Blue Mary的战役地图
Blue Mary的战役地图( hash\(\star\star \))
- 时限:\(1s\) 内存:\(256M\)
Descrption
- \(Blue Mary\) 最近迷上了玩 \(Starcraft\) (星际争霸) 的 \(RPG\) 游戏。她正在设法寻找更多的战役地图以进一步提高自己的水平。
- 由于 \(Blue Mary\) 的技术已经达到了一定的高度,因此,对于用同一种打法能够通过的战役地图,她只需要玩一张,她就能了解这一类战役的打法,然后她就没有兴趣再玩儿这一类地图了。而网上流传的地图有很多都是属于同一种打法,因此 \(Blue Mary\) 需要你写一个程序,来帮助她判断哪些地图是属于同一类的。
- 具体来说,\(Blue Mary\) 已经将战役地图编码为 \(n\times n\) 的矩阵,矩阵的每个格子里面是一个 \(32\) 位(有符号)正整数。对于两个矩阵,他们的相似程度定义为他们的最大公共正方形矩阵的边长。两个矩阵的相似程度越大,这两张战役地图就越有可能是属于同一类的。
Input
- 输入文件的第一行包含一个正整数 \(n\) 。
- 以下 \(n\) 行,每行包含 \(n\) 个正整数,表示第一张战役地图的代表矩阵。
- 再以下 \(n\) 行,每行包含 \(n\) 个正整数,表示第二张战役地图的代表矩阵。
Output
- 输出文件仅包含一行。这一行仅有一个正整数,表示这两个矩阵的相似程度。
Sample Input
3
1 2 3
4 5 6
7 8 9
5 6 7
8 9 1
2 3 4
Sample Output
2
Hint
-
子矩阵:
5 6
8 9
为两个地图的最大公共矩阵
-
对于\(30\%\) 的数据 \(1<=n<=50\) 。
-
对于 \(60\%\) 的数据 \(1<=n<=200\)。
-
对于 \(100\%\) 的数据 \(1<=n<=500\) 。
-
来源:\(luogup4398\)
分析
- 方法一:\(dp\)
- 此题很像线性 \(dp\) 的求最长公共子串。但不同的是此题是二维矩阵,我们可以参照最长公共子串的模型定义出状态:\(dp[x1][y1][x2][y2]\) 表示第一个矩阵的右下角坐标为 \((x1,y1)\) ,第二个矩阵的右下角坐标为 \((x2,y2)\) 公共正方形最大的边长。则有状态转移方程:
- \(dp[x1][y1][x2][y2]=min(dp[x1-1][y1-1][x2-1][y2-1],min(dp[x1][y-1][x2][y2-1],dp[x1-1][y1][x2-1][y2]))\)
- 即 \((x1,y1),(x2,y2)\) 为右下角的最大公共子矩阵要看以当前点的左边,正上,左上为右下角的三个公共矩阵中最小的再加上 \(1\),这个 \(1\) 就是当前点坐在的行和列。
- 这是这类题的典型做法,但实在对不起,\(n\le 100\) ,\(n^4\) 有点悬,不过本服务器还是能压过。
Code
#include<bits/stdc++.h>
const int maxn=101,Inf=0x3f3f3f3f;
int dp[maxn][maxn][maxn][maxn];
int n,a[maxn][maxn],b[maxn][maxn];
void Init(){
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]);
}
void Solve(){
int ans=0;
for(int x1=1;x1<=n;++x1)
for(int y1=1;y1<=n;++y1)
for(int x2=1;x2<=n;++x2)
for(int y2=1;y2<=n;++y2)
if(a[x1][y1]==b[x2][y2]){
dp[x1][y1][x2][y2]=std::min(dp[x1-1][y1-1][x2-1][y2-1],std::min(dp[x1][y1-1][x2][y2-1],dp[x1-1][y1][x2-1][y2]))+1;
ans=std::max(ans,dp[x1][y1][x2][y2]);
}
printf("%d\n",ans);
}
int main() {
Init();
Solve();
return 0;
}
方法二: \(hash\)
-
二维 \(hash\)
-
\(Code\)
#include<bits/stdc++.h> typedef unsigned long long uLL; const int maxn=101,Inf=0x3f3f3f3f; const uLL RowBase=11,ColBase=13; int n; uLL Row[maxn],Col[maxn]; uLL a[maxn][maxn],b[maxn][maxn]; void Init(){ scanf("%d",&n); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%lld",&a[i][j]); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%lld",&b[i][j]); Row[0]=Col[0]=1;//Row:行基数,Col:列基数 for(int i=1;i<=n;++i)//Row[i]表示第i位的以RowBase为进制的基数 Row[i]=Row[i-1]*RowBase; for(int i=1;i<=n;++i) Col[i]=Col[i-1]*ColBase; } uLL Calc(uLL a[][maxn],int x,int y,int h){//二维哈希的标准姿势 return a[x][y]-a[x-h][y]*Col[h]-a[x][y-h]*Row[h]+a[x-h][y-h]*Row[h]*Col[h]; } void Solve(){ for(int i=1;i<=n;++i)//把每一行压缩成一个RowBase进制数 for(int j=1;j<=n;++j) a[i][j]=a[i][j-1]*RowBase+a[i][j]; for(int i=1;i<=n;++i)//压缩列 for(int j=1;j<=n;++j) a[i][j]=a[i-1][j]*ColBase+a[i][j]; std::map<uLL,bool> mp; for(int h=0;h<=n;++h)//正方形边长 for(int i=h;i<=n;++i)//i,j为正方形的右下角 for(int j=h;j<=n;++j) mp[Calc(a,i,j,h)]=1;//赋值为什么都可以,只要有就行 for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) b[i][j]=b[i][j-1]*RowBase+b[i][j]; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) b[i][j]=b[i-1][j]*ColBase+b[i][j]; for(int h=n;h>=0;--h)//注意h可以为0,因为可能没有相同的元素 for(int i=h;i<=n;++i) for(int j=h;j<=n;++j) if(mp.find(Calc(b,i,j,h))!=mp.end()){ printf("%d\n",h);return; } } int main() { Init(); Solve(); return 0; }
hzoi