2018.8.19T3(动态规划,矩阵快速幂)

题目大意:
一张n个点m条边的无向图,边有边权w,从S出发,到T,每个时间店能走一步,求K时间点恰好到T所经过的边权的最小值。
同时有a个怪物
每个怪物有自己的游走路径,按照顺序在这些点直接游走,也是一个时间点移动一步。每个怪物游走的点个数2<=T<=4
求不碰到怪物的情况下,第一问的值
如果不可能输出impossible
C组数据
数据范围
这里写图片描述


k这么大一脸矩阵快速幂优化动态规划
我们考虑 fk,i,j f k , i , j 表示 i i j k k 步经过的最小边权
fk,i,j=min(max(fk1,i,k,wk,j))
k=1 k = 1 时这就是原图的邻接矩阵
这里我们乘法需重新定义
(AB)i,j=min(max(Ai,k,Bk,j)) ( A B ) i , j = m i n ( m a x ( A i , k , B k , j ) )
a=0 a = 0 直接对邻接矩阵快速幂即可
有怪物的时候,我们就把所有到怪物所在点的边设为INF即可

#include<bits/stdc++.h>
using namespace std; 
#define ll long long
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
const int INF = 1e9+7;
struct martix{
    int a[60][60];
}G[13] , A , f;
int n , m , S , T;
ll k;
int num_mon , num_t[35] , att[35][5];
bool vis[60];
ll read()
{
    ll sum = 0;char c = getchar();bool flag = true;
    while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
    while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
    if(!flag)  sum = -sum;
    return sum;
}
void re(martix &a)
{
    rep(i,1,n)
        rep(j,1,n)
            a.a[i][j] = 0;
    return;
}
martix mul(martix a,martix b)
{
    martix ans;
    rep(i,1,n)
        rep(j,1,n)
        {
            ans.a[i][j] = INF;
            rep(k,1,n)
                ans.a[i][j] = min(ans.a[i][j] , max(a.a[i][k] , b.a[k][j]) );
        }
    return ans;
}
void mem(martix &a,martix b){rep(i,1,n)rep(j,1,n) a.a[i][j] = b.a[i][j];}
martix Pow(martix a,ll b)
{
    martix ans;
    mem(ans,a);
    while(b)
    {
        if(b & 1) ans = mul(ans,a);
        a = mul(a,a);
        b >>= 1;
    }
    return ans;
}
void reset(martix &f){rep(i,1,n) rep(j,1,n) f.a[i][j] = INF;}
void init()
{
    n = read();m = read();S = read();T = read();k = read();
    reset(f);
    rep(i,1,m)
    {
        int x = read() , y = read() , z = read();
        f.a[x][y] = f.a[y][x] = min(z,f.a[x][y]);
    }
    num_mon = read();
    rep(i,1,num_mon)
    {
        num_t[i] = read();
        rep(j,1,num_t[i])
        {
            int tmp = j + num_t[i] - 1;
            att[i][tmp%num_t[i]] = read();
        }
    }
    rep(i,1,12)
    {
        mem(G[i>11?0:i],f);
        memset(vis,0,sizeof(vis));
        rep(k,1,num_mon){
            if(!vis[att[k][i%num_t[k]]])
            {
                int x = att[k][i%num_t[k]];
                vis[x] = true;
                rep(j,1,n)
                    G[i>11?0:i].a[j][x] = INF;
            }
        }
    }
    mem(A,G[1]);
    rep(i,2,12) A = mul(A,G[i>11?0:i]);
    int tmp = k / 12;
    if(tmp > 1) A = Pow(A,k/12-1);
    else if(tmp == 0) A = G[1]; 
    if(tmp != 0 && k % 12 != 0)A = mul(A,G[1]);
    rep(i,2,k%12) A = mul(A,G[i]);
    if(A.a[S][T] != INF) printf("%d\n",A.a[S][T]);
    else printf("impossible\n");
    return;
}
int main()
{
    freopen("ishaac.in","r",stdin);
    freopen("ishaac.out","w",stdout);
    int tt = read();
    while(tt--)
    init();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值