【题意】
n个点m条有向边,每个点有一个权值v[i],求一条从入度为0的点到出度为0的点路径上的经过所有点权值之和最小值。保证:
t is guaranteed that each road appears exactly once, and there is no way to return to a previous city. (实际上是告诉无环)
1 ≤ n ≤ 100000, 0 ≤ m ≤ 1000000
【题解】
第一开始拆点用spfa做结果一直T,后来就用记忆化搜索(dp)写了就A了。
拓扑排序+DP我写的会T……发现记忆化搜索是个好东西,既快又好写。看来我以后还是少写拓扑排序吧,反正都可转化为记忆化搜索。
【Code】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=210000;
const int inf=1<<30;
int v[maxn],dout[maxn];
int f[maxn];
vector<int>pre[maxn];
void dp(int x)
{
if(f[x]>-inf)return;
if(pre[x].size()==0)
{
f[x]=v[x];
return;
}
int tn=pre[x].size();
for(int i=0;i<tn;i++)
{
int y=pre[x][i];
dp(y);
if(f[y]+v[x]>f[x])f[x]=f[y]+v[x];
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<=n+1;i++)
{
pre[i].clear();
dout[i]=0;
v[i]=0;
}
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
dout[x]++;
pre[y].push_back(x);
}
for(int i=0;i<=n+1;i++)
f[i]=-inf;
for(int i=1;i<=n;i++)
if(dout[i]==0)
{
pre[n+1].push_back(i);
}
dp(n+1);
printf("%d\n",f[n+1]);
}
return 0;
}