poj-3735-Training little cats-矩阵快速幂

本文介绍了一种利用矩阵快速幂技术解决大规模重复操作序列问题的方法。通过具体实例讲解了如何构造矩阵来模拟给定的操作序列,并展示了如何通过矩阵乘法实现序列的有效重复。文章还特别关注了稀疏矩阵的乘法优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Link:http://poj.org/problem?id=3735

Training little cats

Time Limit: 2000MS      Memory Limit: 65536K
Total Submissions: 11954        Accepted: 2951

Description

     Facer's pet cat just gave birth to a brood of little cats. Having considered the health of those lovely cats, Facer decides to make the cats to do some exercises. Facer has well designed a set of moves for his cats. He is now asking you to supervise the cats to do his exercises. Facer's great exercise for cats contains three different moves:
    g i : Let the ith cat take a peanut.
    e i : Let the ith cat eat all peanuts it have.
    s i j : Let the ith cat and jth cat exchange their peanuts.
    All the cats perform a sequence of these moves and must repeat it m times! Poor cats! Only Facer can come up with such embarrassing idea. 
    You have to determine the final number of peanuts each cat have, and directly give them the exact quantity in order to save them.

Input

    The input file consists of multiple test cases, ending with three zeroes "0 0 0". For each test case, three integers n, m and k are given firstly, where n is the number of cats and k is the length of the move sequence. The following k lines describe the sequence.
    (m≤1,000,000,000, n≤100, k≤100)

Output

    For each test case, output n numbers in a single line, representing the numbers of peanuts the cats have.

Sample Input

3 1 6
g 1
g 2
g 2
s 1 2
g 3
e 2
0 0 0

Sample Output

2 0 1

Source

PKU Campus 2009 (POJ Monthly Contest – 2009.05.17), Facer

思路:

    这个题我们看到m非常大,并且应该是牵涉到线性变化的,故我们自然而然的联想到矩阵快速幂,但是这个题比较难的便是矩阵的构造,因为一开始有n个小猫,我们可以想到nXn的矩阵,那么对于每个小猫有用的是1Xn矩阵,但是这样的话初始是矩阵为零矩阵,无法使用矩阵乘法由此我们修改矩阵为(n+1)X(n+1)矩阵,我们以此题测试数据为例。
    我们默认从0开始!!!!!——————!!!!!
    开始时
    |1 0 0 0|
    |0 0 0 0|
    |0 0 0 0|
    |0 0 0 0|
    我们找一个单位矩阵,用于记录变换
    |1 0 0 0|
    |0 1 0 0|
    |0 0 1 0|
    |0 0 0 1|
    如果给第一只小猫一个peanut的话变换矩阵变成
    |1 1 0 0|
    |0 1 0 0|
    |0 0 1 0|
    |0 0 0 1|
    连续给第二个小猫两次的话则依次为
    |1 1 1 0|
    |0 1 0 0|
    |0 0 1 0|
    |0 0 0 1|

    |1 1 2 0|
    |0 1 0 0|
    |0 0 1 0|
    |0 0 0 1|

    交换1,2小猫的peanut则相当于变换矩阵的对应1列和2列交换
    |1 2 1 0|
    |0 0 1 0|
    |0 1 0 0|
    |0 0 0 1|
    清空2猫的peanut则相当于变换矩阵的2列化为0
    |1 1 0 0|
    |0 1 0 0|
    |0 0 0 0|
    |0 0 0 1|
    综上a猫加一个peanut,变换矩阵M0a++;
    a猫清空peanut,变换矩阵a列化为0;
    a,b猫交换,a列与b列值交换

    本题另一个精彩部分就是对于稀疏矩阵的乘法

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn(105);
int n;
struct matrix
{
    long long mat[maxn][maxn];
    void zero()
    {
        for(int i=0; i<=n; i++)
        {
            for(int j=0; j<=n; j++)
            {
                this->mat[i][j]=0;
            }
        }
    }
    void unit()
    {
        zero();
        for(int i=0; i<=n; i++)
            this->mat[i][i]=1;
    }
    matrix operator * (const matrix &a)const
    {
        matrix res;
        res.zero();
        for(int k=0; k<=n; k++)
        {
            for(int i=0; i<=n; i++)
            {
                if(this->mat[i][k])
                {
                    for(int j=0; j<=n; j++)
                    {
                        res.mat[i][j]+=this->mat[i][k]*a.mat[k][j];
                    }
                }
            }
        }
        return res;
    }
    matrix operator ^(int k)const
    {
        matrix res;
        matrix B=*this;
        res.unit();
        while(k)
        {
            if(k&1)
                res=res*B;
            k>>=1;
            B=B*B;
        }
        return res;
    }

};

int main()
{
    int m,k;
    matrix start;
    matrix operation;

    while(scanf("%d%d%d",&n,&m,&k),n||m||k)
    {
        start.zero();
        start.mat[0][0]=1;
        char cmd;
        operation.unit();
        int a,b;
        while(k--)
        {
            getchar();
            cmd=getchar();
            switch(cmd)
            {
            case 'g':
                scanf("%d",&a);
                operation.mat[0][a]++;
                break;
            case 'e':
                scanf("%d",&a);
                for(int i=0; i<=n; i++)
                    operation.mat[i][a]=0;
                break;
            default:
                scanf("%d%d",&a,&b);
                for(int i=0; i<=n; i++)
                    swap(operation.mat[i][a],operation.mat[i][b]);
                break;
            }
        }
        start=start*(operation^m);
        for(int i=1; i<n; i++)
            cout<<start.mat[0][i]<<" ";
        cout<<start.mat[0][n]<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值