FZOJ 4106 哈

一个图上的DP啊,怪我太年轻又没看出来考试的时候直接给它爆0了?

\(f[i][j][k]\)表示从\(i\)点到\(j\)经过\(k\)条边。由于走过的边要求权值升序,不妨换个思路(不直接枚举状态),考虑直接把所有边从小到大排序然后直接枚举每条边,然后DP,注意因为要求的是经过 不多于 \(c\)条边的代价,而两点间的最短路径一定不会超过\(n\)条边所以枚举\(k\)的时候\(1-n\)就好了。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=5009,INF=1<<30;
int n,m,q,f[151][151][151];
struct Edge
{
	int x,y,z;
	bool operator < (const Edge &A)const
	{
		return z<A.z;
	}
}g[N];

void init()
{
	scanf("%d %d %d",&n,&m,&q);
	for (int i=1;i<=m;i++)
		scanf("%d %d %d",&g[i].x,&g[i].y,&g[i].z);
	sort(g+1,g+1+m);
}

void work()
{
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			for (int k=0;k<=n;k++)
				f[i][j][k]=INF;
	for (int i=1;i<=n;i++)
		f[i][i][0]=0;
	for (int i=0;i<=m;i++)
		for (int j=1;j<=n;j++)
			for (int k=0;k<n;k++)
				if(f[j][g[i].x][k]!=INF)
					f[j][g[i].y][k+1]=min(f[j][g[i].y][k+1],f[j][g[i].x][k]+g[i].z);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			for (int k=1;k<=n;k++)
				f[i][j][k]=min(f[i][j][k-1],f[i][j][k]);
	while(q--)
	{
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);
		printf("%d\n",f[a][b][min(c,n)]==INF?-1:f[a][b][min(c,n)]);
	}
}

int main()
{
	init();
	work();
	return 0;
}
posted @ 2020-04-23 06:16  With_penguin  阅读(79)  评论(0编辑  收藏  举报