首页 > 代码库 > [ACM] POJ 3318 Matrix Multiplication (随机化算法)

[ACM] POJ 3318 Matrix Multiplication (随机化算法)

Matrix Multiplication
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 16118 Accepted: 3485

Description

You are given three n × n matrices AB and C. Does the equation A × B = C hold true?

Input

The first line of input contains a positive integer n (n ≤ 500) followed by the the three matrices AB and respectively. Each matrix‘s description is a block of n × n integers.

It guarantees that the elements of A and B are less than 100 in absolute value and elements of C are less than 10,000,000 in absolute value.

Output

Output "YES" if the equation holds true, otherwise "NO".

Sample Input

2
1 0
2 3
5 1
0 8
5 1
10 26

Sample Output

YES

Hint

Multiple inputs will be tested. So O(n3) algorithm will get TLE.

Source

POJ Monthly--2007.08.05, qzc


解题思路:

题意为给定N*N的矩阵 A,B,C , 如果A*B=C,输出YES,否则输出NO。

第一次接触随机化算法,真的很奇妙。利用计算机随机给出符合题意的值,再根据题意关系计算是否出现矛盾,如果出现,则不成功。比如本题,就随机给出一个行数row,一个列数col, 那么必须有 

for(int i=1;i<=n;i++)
            temp+=A[row][i]*B[i][col];   temp==C[row][col] ; 

如果 出现不相等,那么肯定是不符合A*B=C的。但是这里随机的次数是个问题,开大了怕超时,开小了怕得不出正确答案,所以尽量比所有可能情况多一点。这种弊端会在下面第二种方法中解决。

百度百科说的很好:

随机化算法是这样一种算法,在算法中使用了随机函数,且随机函数的返回值直接或者间接的影响了算法的执行流程或执行结果。随机化算法基于随机方法,依赖于概率大小。

在我们的生活中,人们经常会去掷色子来看结果,投硬币来决定行动,这就牵涉到一个问题:随机。

这种算法看上去是凭着运气做事,其实,随机化算法是有一定的理论基础的,我们可以想象,在[1,10000]这个闭区间里,随机1000次,随机到2这个数的几率是多大(约为0.1),何况1000次的随机在计算机程序中仅仅是一眨眼的功夫。可以看出,随机化算法有着广阔的前景。只是由于随机化算法比较难于掌控,所以并不是很多人都接触过他,但肯定有很多人都听说过。

例一

下面,我们就随机化问题,举一个例子:
一个长度在4..10的字符串中,需要判定是否可以在字符串中删去若干字符,使得改变后字符串符合以下条件之一:
(1)AAAA;(2)AABB;(3)ABAB;(4)ABBA。
例如:长度为6字符串“POPKDK”,若删除其中的“O”,“D”两个字母,则原串变为:“PPKK”,符合条件(2)AABB。
分析:
这道题很容易想到一种算法:运用排列组合:枚举每4个字母,然后逐一判断。算法是可行的,但是如果需要题目中加上一句话:需要判断n个字符串,且n<=100000,那么这样的耗时是不能让人忍受的,因为在枚举的过程中,是非常浪费时间的。
①:这里是指信息学中要求算法的普遍运算时间为:1000ms)
所以这道题有可能可以借助于随机化算法,下面我们来算一下在10个字符中取4个字符一共有多少种取法:C(4,10)=210。那么很容易得知,随机化算法如果随机300次,能得到的结果基本上就正确了(概率为1-(209/210)^300,约为0.76),而随机时的时间消耗是O(1),只需要判断没有随机重复即可,判重的时间复杂度也为O(1),并且最多随机300次,这样就可以有效地得到答案,最大运算次数为:O(300n),这是在计算机的承受范围内(1000ms)的。
从这里就能看出,随机化算法是一个很好的概率算法,但是它并不能保证正确,而且它单独使用的情况很少,大部分是与其他的算法:例如贪心、搜索等配合起来运用。

例二

排序问题。快速排序是排序方法中较为便捷的方法之一,但是由于它极不稳定,最好的时候时间复杂度为O(n㏒n),这里的㏒是指以2为底的对数运算。最坏的时候能达到与普通排序方法一样的O(n^2)。
而制约快速排序的有两个:一是数据,越无序的数据,快排的速度越快;二是中间点的枚举。
因为两个制约条件都与随机有着不可分开的关系。
所以,在快速排序中加入随机化算法无疑是十分重要的。
运用在:
(1)数据读入时,随机排放数据位置。
(2)中间点的枚举进行多次随机化后决定。
这样就基本上将快速排序的时间复杂度维持在最好状态。

第一种方法代码:

#include <iostream>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
const int maxn=510;
int A[maxn][maxn];
int B[maxn][maxn];
int C[maxn][maxn];
int n;


void input(int c[maxn][maxn])
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&c[i][j]);
}

bool ok()
{
    int row,col;//随机行数,列数
    for(int k=1;k<=30000;k++)
    {
        row=rand()%n+1;
        col=rand()%n+1;
        int temp=0;
        for(int i=1;i<=n;i++)
            temp+=A[row][i]*B[i][col];
        if(temp!=C[row][col])
                return false;
    }
    return true;
}

int main()
{
    scanf("%d",&n);
    srand(time(NULL));
    input(A);
    input(B);
    input(C);
    if(ok())
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;

    return 0;
}

第二种方法:

左乘一个一行N列的向量 r[],  那么 肯定有  r*A*B=r*C 

代码:

#include <iostream>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
const int maxn=510;
int A[maxn][maxn];
int B[maxn][maxn];
int C[maxn][maxn];
int r[maxn];
int rA[maxn];
int rAB[maxn];
int rC[maxn];
int n;


void input(int c[maxn][maxn])
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&c[i][j]);
}

bool ok()
{
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
    {
        rA[j]+=r[i]*A[i][j];
        rC[j]+=r[i]*C[i][j];
    }
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
        rAB[j]+=rA[i]*B[i][j];

    for(int i=1;i<=n;i++)
        if(rAB[i]!=rC[i])
        return false;
    return true;
}

int main()
{
    scanf("%d",&n);
    srand(time(NULL));
    input(A);
    input(B);
    input(C);
    memset(rA,0,sizeof(rA));
    memset(rAB,0,sizeof(rAB));
    memset(rC,0,sizeof(rC));
    for(int i=1;i<=n;i++)
        r[i]=rand()%99+1;
    if(ok())
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
    return 0;
}