HDU 6331 Problem M. Walking Plan(分块+Floyd)

22 篇文章 0 订阅
10 篇文章 0 订阅

题目链接

Problem M. Walking Plan

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 883    Accepted Submission(s): 322


 

Problem Description

There are n intersections in Bytetown, connected with m one way streets. Little Q likes sport walking very much, he plans to walk for q days. On the i-th day, Little Q plans to start walking at the si-th intersection, walk through at least ki streets and finally return to the ti-th intersection.
Little Q's smart phone will record his walking route. Compared to stay healthy, Little Q cares the statistics more. So he wants to minimize the total walking length of each day. Please write a program to help him find the best route.

Input

The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there are 2 integers n,m(2≤n≤50,1≤m≤10000) in the first line, denoting the number of intersections and one way streets.
In the next m lines, each line contains 3 integers ui,vi,wi(1≤ui,vi≤n,ui≠vi,1≤wi≤10000), denoting a one way street from the intersection ui to vi, and the length of it is wi.
Then in the next line, there is an integer q(1≤q≤100000), denoting the number of days.
In the next q lines, each line contains 3 integers si,ti,ki(1≤si,ti≤n,1≤ki≤10000), describing the walking plan.

Output

For each walking plan, print a single line containing an integer, denoting the minimum total walking length. If there is no solution, please print -1.

Sample Input

 

2

3 3

1 2 1

2 3 10

3 1 100

3

1 1 1

1 2 1

1 3 1

2 1

1 2 1

1

2 1 1

 

 

Sample Output

 

111

1

11

-1

 题目:

给你一个有向图,然后有q次询问,每一次询问s,t,k,表示s->t的经过至少k条边的最短路长度

解析:

官方题解:

设G[i][j]表示i一步到j的最短路,f[t][i][j]表示i经过恰好t条边到达j的最短路,则f[t][i][j]=\min(f[t-1][i][k]+G[k][j])。f具有更强的性质,即f[t][i][j]=\min(f[x][i][k]+f[t-x][k][j])。将合并的过程看作乘法,则f[t]=f[x]f[t-x]=G^t​​。

注意到k\leq 10000,设A=\lfloor\frac{k}{100}\rfloor,B=k\bmod 100$,将每个询问s,t,k拆成两步:

  1. 从s出发经过恰好100*A条边到达某个点u。
  2. 从u出发经过至少B条边到达t。

a[i]=G^{100i},b[i]=G^i,那么a[i]即为经过恰好100i条边的最短路,对于b[i]需要再进行一次Floyd得到至少i条边的最短路。

枚举中间点u,则ans=\min(a[A][s][u]+b[B][u][t])

时间复杂度O(n^3\sqrt{k}+qn)

 

首先分块,这里把对k进行分块。但是不是简单的将询问分成k'<\sqrt{k}和k'>\sqrt{k}的情况,因为其实这里好像也没有什么空间的问题,

k\leq 10000,数组完全够用。所以我们把他进行纵向的分块将每一次查询分成两块来处理,一块是A=\lfloor\frac{k}{\sqrt{k}}\rfloorB=k\bmod \sqrt{k}$

即上面题解说的两步。

然后就是求A,B的数组。这里其实只需要f[t][i][j]=\min(f[t-1][i][k]+G[k][j])这一条性质就可以了。其实这个好像更像是动态规划。G[k][j]其实就是初始的图,但是注意,要把G[i][i]=INF,因为i走一步是走不到i的。然后我们就可以根据这个式子对于第一维[t]从下往上递推就可以了,对于里面的那两维[i][j]我们可以用Floyd()来做,所以总共和在一起就是4个循环,O(n^3\sqrt{k})的复杂度。然后求出B,与上面类似,只不过这里的G[k][j]变成了A[100][k][j],道理同上。然后我们还需要让B变成至少i条的最短路,这个其实就是很简单的dp,B[k][j][i]=min(B[k+1][j][i]) j∈[99,0]。最后询问的按照题解说的那样,遍历n项求min求行了。

我看很多大佬都是把A,B当矩阵来求的,代码写的很简单。上面题解也有说,把它当成矩阵,我们只需要重载矩阵的*,重载的代码其实就是Floyd的代码。

我这里代码上是A=Gi,B=G100i,

#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#define lch root<<1
#define rch (root<<1)|1
#define mk make_pair
#define pb push_back
using namespace std;
typedef long long ll;

const int MAXN = 1e2+2;
#define Max 60
#define INF 0x3f3f3f3f
typedef struct graph *Graph;
typedef pair<int,int> PII;
typedef struct node
{
    int s,x;
    int k;
    int dis;
}node;
typedef struct graph
{
    int e , n;
    int data[Max][Max];
}graph;
int A[MAXN][Max][Max];
//int Path[Max][Max];
int B[MAXN][Max][Max];



void Floyd(Graph g)
{
    int i , j , k , w;
	for(w = 0 ; w < MAXN ; w++)
		for(i = 1 ; i <= g->n ; i++)
		{
			for(j = 1 ; j <= g->n ; j++)
			{
				if(i==j&&w==0) A[w][i][j]=0;
				else A[w][i][j]=INF;

			}
		}
		for(w = 1 ; w < MAXN ; w++)
		{
			for(i = 1 ; i <= g->n ; i++)
			{
				for(j = 1 ; j <= g->n ; j++)
				{
					for(k = 1 ; k <= g->n ; k++)
					{
						if(A[w][j][k] > A[w-1][j][i] + g->data[i][k])
						{
							A[w][j][k] = A[w-1][j][i] + g->data[i][k];
						}
					
					}
				}
			}
		}
}

void Floyd100(Graph g)
{
	int i , j , k , w;
	for(w = 0 ; w < MAXN ; w++)
		for(i = 1 ; i <= g->n ; i++)
		{
			for(j = 1 ; j <= g->n ; j++)
			{
				if(i==j&&w==0) B[w][i][j]=0;
				else B[w][i][j]=INF;

			}
		}

	for(w = 1; w < MAXN ; w++)
	{
		for(i = 1 ; i <= g->n ; i++)
		{
			for(j = 1 ; j <= g->n ; j++)
			{
				for(k = 1 ; k <= g->n ; k++)
				{
					if(B[w][j][k]>B[w-1][j][i]+A[100][i][k])
					{
						B[w][j][k]=B[w-1][j][i]+A[100][i][k];
					}
				}
			}
		}
	}
}


void Floydmin(Graph g)
{
	int i,j,w,k;
	for(w = MAXN-2 ; w >= 0 ; w--)
		for(i = 1 ; i <= g->n ; i++)
		{
			for(j = 1 ; j <= g->n ; j++)
			{
				A[w][i][j]=min(A[w][i][j],A[w+1][i][j]);

			}
		}
}


graph g;
int ans=INF;

int main()
{
	int t;
	int n,m;
	scanf("%d",&t);

	while(t--)
    {

        scanf("%d%d",&n,&m);
        g.n=n;
        g.e=m;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                /*if(i==j) g.data[i][j]=0;
                else */g.data[i][j]=INF;    //data[i][j]表示i->j走一步的最短路
        }

        for(int i=0;i<m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            g.data[x][y]=min(w,g.data[x][y]);
        }


        Floyd(&g);
		Floyd100(&g);
		Floydmin(&g);
        /*printf("%d\n",B[1][2]);
        showPath(1,2);*/



        int q;
        scanf("%d",&q);
        int s,e,k;
        
        for(int i=0;i<q;i++)
        {

            scanf("%d%d%d",&s,&e,&k);
           
            ans=INF;
			
            for(int i=1;i<=g.n;i++)
            {
                ans=min(ans,B[k/100][s][i]+A[k%100][i][e]);
            }
            if(ans==INF) printf("-1\n");
            else printf("%d\n",ans);
        }
    }
	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值