1. 程式人生 > >洛谷P3953 逛公園(spfa+記憶化搜尋)

洛谷P3953 逛公園(spfa+記憶化搜尋)

P3953 逛公園

題目描述 策策同學特別喜歡逛公園。公園可以看成一張N個點M條邊構成的有向圖,且沒有 自環和重邊。其中1號點是公園的入口,N號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。 策策每天都會去逛公園,他總是從1號點進去,從N號點出來。 策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到NN號點的最短路長為d,那麼策策只會喜歡長度不超過d + K的路線。 策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎? 為避免輸出過大,答案對P取模。 如果有無窮多條合法的路線,請輸出-1。

輸入輸出格式 輸入格式: 第一行包含一個整數 TT, 代表資料組數。 接下來TT組資料,對於每組資料: 第一行包含四個整數 N,M,K,P,每兩個整數之間用一個空格隔開。 接下來MM行,每行三個整數a_i,b_i,c_i代表編號為a_i,b_i的點之間有一條權值為 c_i的有向邊,每兩個整數之間用一個空格隔開。

輸出格式: 輸出檔案包含 T 行,每行一個整數代表答案。

輸入輸出樣例 輸入樣例#1: 2 5 7 2 10 1 2 1 2 4 0 4 5 2 2 3 2 3 4 1 3 5 2 1 5 3 2 2 0 10 1 2 0 2 1 0 輸出樣例#1: 3 -1

分析:先求出每個點i到n的最短路dis[i],即對反向圖從n開始求最短路,然後考慮dp,設f[i][k]表示在i點使得dis[to]+val-dis[i]<=k的路徑方案數,轉移一下就行,如果碰到0環就返回-1;

程式碼

#include <cstdio>
#include <queue>
#include <cstring>
#include <string>
#define N 200005
#define inf 1e9
using namespace std;

struct arr
{
	int to,nxt,w;
}a[N],b[N];
int n,m,k,p,ls1[N],ls2[N],l;
int dis[N];
long long f[N][60];
bool vis[N],v[N][60];

void add(int x, int y, int w)
{
	a[++l].to = y;
	a[l].w = b[l].w = w;
	a[l].nxt = ls1[x];
	b[l].to = x;
	b[l].nxt = ls2[y];
	ls1[x] = ls2[y] = l;
}

void spfa()
{
	queue<int> q;
	for (int i = 1; i <= n; i++)
	{
		dis[i] = inf;
		vis[i] = false;
	}
	while (q.size()) q.pop();
	dis[n] = 0;
	vis[n] = true;
	q.push(n);
	while (q.size())
	{
		int u = q.front();
		q.pop();
		vis[u] = false;
		for (int i = ls2[u]; i; i = b[i].nxt)
			if (dis[b[i].to] > dis[u] + b[i].w)
			{
				dis[b[i].to] = dis[u] + b[i].w;
				if (!vis[b[i].to])
				{
					vis[b[i].to] = true;
					q.push(b[i].to);
				}
			}
	}
}

int dfs(int x, int k)
{
	if (v[x][k]) return -1;
	if (f[x][k]) return f[x][k];
	v[x][k] = true;
	if (x == n) f[x][k] = 1;
	for (int i = ls1[x]; i; i =a[i].nxt)
		if (dis[a[i].to] + a[i].w - dis[x] <= k)
		{
			int tmp = dfs(a[i].to, k - (dis[a[i].to] + a[i].w - dis[x]));
			if (tmp == -1) return -1;
				else f[x][k] = (f[x][k] + tmp) % p;	
		}
	v[x][k] = false;
	return f[x][k];
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		l = 0;
		memset(ls1, 0, sizeof(ls1));
		memset(ls2, 0, sizeof(ls2));
		memset(f, 0, sizeof(f));
		scanf("%d%d%d%d", &n, &m, &k, &p);
		for (int i = 1; i <= m; i++)
		{
			int x, y, w;
			scanf("%d%d%d", &x, &y, &w);
			add(x, y, w);
		}
		spfa();
		memset(v, false, sizeof(v));
		printf("%d\n", dfs(1, k));
	}
}