zoukankan      html  css  js  c++  java
  • [BZOJ1706]:[usaco2007 Nov]relays 奶牛接力跑

    题目传送门


    题目描述

    FJN头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点自然是在牧场中现有的T条跑道上。农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点${I1}_i$${I2}_i$。每个交汇点都是至少两条跑道的端点。奶牛们知道每条跑道的长度${length}_i$,以及每条跑道连结的交汇点的编号并且,没有哪两个交汇点由两条不同的跑道直接相连。你可以认为这些交汇点和跑道构成了一张图。为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道。


    输入格式

    1行:4个用空格隔开的整数:NTS,以及E
    2...T+1行:第i+13个以空格隔开的整数:${length}_i$${I1}_i$,以及${I2}_i$,描述了第i条跑道。


    输出格式

    1行:输出1个正整数,表示起点为S、终点为E,并且恰好经过N条跑道的路径的最小长度。


    样例

    样例输入:

    2 6 6 4
    11 4 6
    4 4 8
    8 4 9
    6 6 8
    2 6 9
    3 8 9

    样例输出:

    10


    数据范围与提示

    2≤N1,000,000

    2T100

    1${I1}_i$1,0001${I2}_i$1,0001${length}_i$1,000


    题解

    根据矩阵乘法的性质,单位矩阵每乘一次转移矩阵就相当与走了一步,故考虑矩阵乘法。

    我们可以发现计算最短路径的Floyd算法结构和矩阵乘法是类似的,但是内部的加法与乘法变为了取最小值与加法。

    可以发现不管是加法乘法还是取最小值,其都满足矩阵乘法要求的结合律与分配律, 所以我们可以更改矩阵乘法的定义,利用邻接矩阵做快速幂即可。

    1≤${I1}_i$≤1,000,1≤${I2}_i≤1,000$显然是不能接受的,但是2≤T≤100令人舒爽,所以需要离散化一手即可。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int ans[1001][1001],Map[1001][1001],flag[1001][1001];
    int lsh[1001],cnt;
    void _doudou()//单位矩阵乘转移矩阵
    {
        for(int i=1;i<=cnt;i++)
        	for(int j=1;j<=cnt;j++)
        		flag[i][j]=ans[i][j],ans[i][j]=0;
    	memset(ans,0x3f,sizeof(ans));
        for(int i=1;i<=cnt;i++)
        	for(int j=1;j<=cnt;j++)
        		for(int k=1;k<=cnt;k++)
        			ans[i][j]=min(ans[i][j],flag[i][k]+Map[k][j]);//记得是min
    }
    void _wzc()//转移矩阵自乘
    {
        for(int i=1;i<=cnt;i++)
        	for(int j=1;j<=cnt;j++)
        		flag[i][j]=Map[i][j],Map[i][j]=0;
    	memset(Map,0x3f,sizeof(Map));
        for(int i=1;i<=cnt;i++)
        	for(int j=1;j<=cnt;j++)
        		for(int k=1;k<=cnt;k++)
        			Map[i][j]=min(Map[i][j],flag[i][k]+flag[k][j]);
    }
    void pre_work()//初始化
    {
    	memset(Map,0x3f,sizeof(Map));
    	memset(ans,0x3f,sizeof(ans));
    	for(int i=1;i<=1000;i++)
    		ans[i][i]=0;
    }
    int main()
    {
    	pre_work();
    	int n,m,s,t;
    	scanf("%d%d%d%d",&n,&m,&s,&t);
    	for(int i=1;i<=m;i++)
    	{
    		int x,y,v;
    		scanf("%d%d%d",&v,&x,&y);
    		if(!lsh[x])lsh[x]=++cnt;
    		if(!lsh[y])lsh[y]=++cnt;
    		Map[lsh[x]][lsh[y]]=Map[lsh[y]][lsh[x]]=v;
    	}
        while(n)//快速幂
        {
            if(n&1)_doudou();
            _wzc();
            n>>=1;
        }
        cout<<ans[lsh[s]][lsh[t]];
    	return 0;
    }
    

    rp++

  • 相关阅读:
    第一次博客作业
    编辑器、编译器、文件、IDE等常见概念辨析
    树、二叉树、查找知识点总结
    二叉排序树
    线性表知识点总结
    c语言文件
    第二次博客作业: 函数+进制转换器v1.0beta
    python作业1
    c语言知识
    第一次博客作业
  • 原文地址:https://www.cnblogs.com/wzc521/p/11204750.html
Copyright © 2011-2022 走看看