luogu P3953 逛公园

这题是在图上跑DP并借助了一些最短路的信息。

  • (30pts:)
    最短路计数,略

  • (70pts:)
    考虑没有(0)边时,设(dis[i])表示从(1)(i)的最短距离,(f[i][j])表示(1)到第(i)个点距离为(dis[i]+j)的方案数,由于没有(0)边,所以发现把(dis[i])排序以后(DP)的顺序就对了。

  • (100pts:)
    (dis[i])表示反向图上从(n)(i)的最短距离,(f[i][j])表示反向图上(n)到第(i)个点距离为(dis[i]+j)的方案数。然后发现每个点只会被正向图上它的后继节点更新,我们可以用记忆化搜索保证DP顺序,中途可以记一个数组表示这个状态在不在栈里,记搜时如果当前访问的点已经在栈内,就说明形成了(0)环,那么这个状态的方案数就是无限多个。

(tips:)遇到一些DP顺序难以枚举的题(特别是图),可以考虑反向图和记忆化搜索,有时可以很好的解决这些问题哦QWQ。

(70pts)代码:

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

using namespace std;

const int N=100009,INF=1<<30;
int n,m,K,P,id[N],f[N][60];

struct G
{
	int head[N],cnt,dis[N],vis[N];
	priority_queue <pair<int,int> > q;
	struct Edge
	{
		int nxt,to,w;
	}g[N*2];
	
	void clear()
	{
		memset(head,0,sizeof(head));
		cnt=0;
	}
	void add(int from,int to,int w)
	{
		g[++cnt].nxt=head[from];
		g[cnt].to=to;
		g[cnt].w=w;
		head[from]=cnt;
	}
	void Dijkstra(int S)
	{
		for (int i=1;i<=n;i++)
			dis[i]=INF,vis[i]=0;
		dis[S]=0,q.push(make_pair(0,S));
		while(!q.empty())
		{
			int x=q.top().second;q.pop();
			if(vis[x])
				continue;
			vis[x]=1;
			for (int i=head[x];i;i=g[i].nxt)
			{
				int v=g[i].to;
				if(dis[v]>dis[x]+g[i].w)
				{
					dis[v]=dis[x]+g[i].w;
					q.push(make_pair(-dis[v],v));
				}
			}
		}
	}
}A,B;

void init()
{
	scanf("%d %d %d %d",&n,&m,&K,&P);
	int x,y,z;
	A.clear(),B.clear();
	memset(f,0,sizeof(f));
	for (int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		A.add(x,y,z);
		//B.add(y,x,z);
	}
}

bool cmp(int a,int b)
{
	return A.dis[a]<A.dis[b];
}

void ADD(int &x,int y)
{
	x=(x+y)%P;
}

void work()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		init();
		A.Dijkstra(1);
		//B.Dijkstra(n);
		for (int i=1;i<=n;i++)
			id[i]=i;
		sort(id+1,id+1+n,cmp);
		f[1][0]=1;
		for (int i=0;i<=K;i++)
			for (int j=1;j<=n;j++)
				for (int k=A.head[id[j]];k;k=A.g[k].nxt)
				{
					int v=A.g[k].to,fuck=i+A.dis[id[j]]+A.g[k].w-A.dis[v];
					if(fuck<=K)
						ADD(f[v][fuck],f[id[j]][i]);
				}
		int ans=0;
		for (int i=0;i<=K;i++)
			ADD(ans,f[n][i]);
		printf("%d
",ans);
	}
}

int main()
{
	work();
	return 0;
}

(100pts)代码:

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

using namespace std;

const int N=100009,INF=1<<30;
int n,m,K,P,id[N],f[N][60],ins[N][60];

struct G
{
	int head[N],cnt,dis[N],vis[N];
	priority_queue <pair<int,int> > q;
	struct Edge
	{
		int nxt,to,w;
	}g[N*2];
	
	void clear()
	{
		memset(head,0,sizeof(head));
		cnt=0;
	}
	void add(int from,int to,int w)
	{
		g[++cnt].nxt=head[from];
		g[cnt].to=to;
		g[cnt].w=w;
		head[from]=cnt;
	}
	void Dijkstra(int S)
	{
		for (int i=1;i<=n;i++)
			dis[i]=INF,vis[i]=0;
		dis[S]=0,q.push(make_pair(0,S));
		while(!q.empty())
		{
			int x=q.top().second;q.pop();
			if(vis[x])
				continue;
			vis[x]=1;
			for (int i=head[x];i;i=g[i].nxt)
			{
				int v=g[i].to;
				if(dis[v]>dis[x]+g[i].w)
				{
					dis[v]=dis[x]+g[i].w;
					q.push(make_pair(-dis[v],v));
				}
			}
		}
	}
}A,B;

void init()
{
	scanf("%d %d %d %d",&n,&m,&K,&P);
	int x,y,z;
	A.clear(),B.clear();
	memset(f,-1,sizeof(f));
	for (int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		A.add(x,y,z);
		B.add(y,x,z);
	}
}

bool cmp(int a,int b)
{
	return A.dis[a]<A.dis[b];
}

void ADD(int &x,int y)
{
	x=(x+y)%P;
}

int dp(int x,int k)
{
	if(k<0)
		return 0;
	if(ins[x][k])
		return -1;
	if(f[x][k]!=-1)
		return f[x][k];
	f[x][k]=(x==n)?1:0;
	ins[x][k]=1;
	for (int i=A.head[x];i;i=A.g[i].nxt)
	{
		int v=A.g[i].to,fuck=k+B.dis[x]-A.g[i].w-B.dis[v];
		if(dp(v,fuck)==-1)
			return f[x][k]=-1;
		ADD(f[x][k],dp(v,fuck));
	}
	ins[x][k]=0;
	return f[x][k];
}

void work()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		init();
		//A.Dijkstra(1);
		B.Dijkstra(n);
		memset(ins,0,sizeof(ins));
		printf("%d
",dp(1,K));
	}
}

int main()
{
	work();
	return 0;
}