HDU 2276 矩阵快速幂,解法:1循环矩阵31ms,2普通矩阵327ms。

http://acm.hdu.edu.cn/showproblem.php?pid=2276

Kiki & Little Kiki 2

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2634    Accepted Submission(s): 1383


Problem Description
There are n lights in a circle numbered from 1 to n. The left of light 1 is light n, and the left of light k (1< k<= n) is the light k-1.At time of 0, some of them turn on, and others turn off.
Change the state of light i (if it's on, turn off it; if it is not on, turn on it) at t+1 second (t >= 0), if the left of light i is on !!!Given the initiation state, please find all lights’ state after M second. (2<= n <= 100, 1<= M<= 10^8)

Input
The input contains one or more data sets. The first line of each data set is an integer m indicate the time, the second line will be a string T, only contains '0' and '1' , and its length n will not exceed 100. It means all lights in the circle from 1 to n.
If the ith character of T is '1', it means the light i is on, otherwise the light is off.

Output
For each data set, output all lights' state at m seconds in one line. It only contains character '0' and '1.
 

Sample Input
  
  
1 0101111 10 100000001
 

Sample Output
  
  
1111000 001000010
题意: 这些灯是一块变的,他们都是根据前一个灯 的状态,确定是否变化后,再一起变化(该变的变,不该变的不变)。不是一个一个变的。

思路:1的前一个若是1,则变为0,若是0,则变为1.

           0的前一个若是1,则变为1,若是0,则变为0.

前一位   后一位  得   后一位变化为***:

1  1 得 0;

0  1 得 1;

1  0 得 1;

0  0 得 0 ;

能看出是相加后%2;也就是  ^  运算,

初始矩阵           *      系数矩阵           =   下一个矩阵

0 1 0 1 1 1 1            1 1 0 0 0 0 0           1 1 1 1 0 0 0

0 0 0 0 0 0 0            0 1 1 0 0 0 0           0 0 0 0 0 0 0

0 0 0 0 0 0 0     *     0 0 1 1 0 0 0     =    0 0 0 0 0 0 0  

0 0 0 0 0 0 0            00 1 1 0 0           0 0 0 0 0 0 0

0 0 0 0 0 0 0            0 00 1 1 0           0 0 0 0 0 0 0

0 0 0 0 0 0 0            00 0 0 1 1           0 0 0 0 0 0 0

0 0 0 0 0 0 0            1 0 0 0 0 0 1           0 0 0 0 0 0 0

所以初始矩阵乘以系数矩阵的K次方即可,矩阵快速幂轻松搞定。

奉上我的AC代码:(时间很长,还有31ms的代码在下面介绍)

Status Accepted
Time 327ms
Memory 1992kB
Length 1064
Lang G++
Submitted

#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 50000
#define LL long long
using namespace std;
const int m=200907;
int n;
char s[101];
struct node
{
    int a[101][101];
} a,ans;
node operator * (node b,node c)
{
    node d;
    memset(d.a,0,sizeof(d.a));
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            for(int k=0; k<n; k++)
                d.a[i][j]^=b.a[i][k]&c.a[k][j];//用运算符会比加法乘法更快一点,(用加法乘法和取余,耗时358ms)
    return d;
}
void init()
{
    memset(a.a,0,sizeof(a.a));
    memset(ans.a,0,sizeof(ans.a));
    scanf("%s",s);
    n=strlen(s);
    for(int i=0;i<n;i++)
       ans.a[0][i]=s[i]-'0';
       a.a[0][0]=1;
       a.a[n-1][0]=1;
       for(int i=1;i<n;i++)
          {
              a.a[i][i]=1;
              a.a[i-1][i]=1;
          }
}
int main()
{
    int k;
    while(~scanf("%d",&k))
    {
        init();
        while(k)
        {
            if(k%2) ans= ans*a;
            a= a*a;
            k=k>>1;
        }
        for(int i=0;i<n;i++)
        printf("%d",ans.a[0][i]);
        printf("\n");
    }
}
优化:想想是哪个地方浪费时间了,矩阵乘法可是n^3,n范围还是100,非常大了。

有一种办法能让n^3变成n^2的,因为系数矩阵是个循环矩阵,循环矩阵乘以循环矩阵,结果还是循环矩阵。

(因为循环矩阵a[i][i] = a[i-1][i-1] , 所以只需要计算出第0行然后递推剩下的其他行就ok了)

计算第一行是n^2复杂度。

for(int i=1;i<len;i++) for(int j=0;j<len;j++) d.a[i][j]=d.a[i-1][(j-1+len)%len];

这个也是n^2的复杂度。

相对于n^3复杂度大大降低。

所以刚开始不管字符串是啥,先把系数矩阵的K次方求出来,乘以初始矩阵(字符串初始矩阵)。

还有一个问题,系数矩阵的k次方存在哪里呢,答案是用 单位矩阵 和 他 相乘这样就能保证他们怎么乘都是循环矩阵了

for(int i=0;i<len;i++)
        ans.a[i][i]=1;

这就是单位矩阵主对角线上的都是1,其他是0;任何矩阵乘以单位矩阵都不变。

如果不弄单位矩阵,估计会麻烦一点点了。


奉上我的AC代码:

Status Accepted
Time 31ms
Memory 1860kB
Length 1180
Lang G++
Submitted

#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 50000
#define LL long long
using namespace std;
int len;
struct node
{
    int a[101][101];
}a,ans;
node operator * (node b,node c)
{
    node d;
    memset(d.a,0,sizeof(d.a));
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
            d.a[0][i]^=b.a[0][j]&c.a[j][i];
        for(int i=1;i<len;i++)
            for(int j=0;j<len;j++)
               d.a[i][j]=d.a[i-1][(j-1+len)%len];
        return d;
}
void init()
{
    memset(a.a,0,sizeof(a.a));
    memset(ans.a,0,sizeof(ans.a));
    for(int i=0;i<len;i++)
    {
        ans.a[i][i]=1;
        a.a[i][i]=1;
        a.a[i][i+1]=1;
    }
    a.a[len-1][0]=1;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        char s[101];
        scanf("%s",s);
        len=strlen(s);
        init();
        while(n)
        {
            if(n%2) ans=ans*a;
            a=a*a;
            n=n>>1;
        }
        for(int i=0;i<len;i++)
        {
            int x=0;
            for(int j=0;j<len;j++)
                x^=(s[j]-'0')&ans.a[j][i];
            printf("%d",x);
        }
        printf("\n");
    }
}







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值