【分析】
求 N 天最小,考虑递推。
设
边界为:
动态转移方程为
f[i]=min(f[j]+MinPath(j,i))+k
其中
MinPath
直接用最短路求。
时间复杂度:
O(n2k)
空间复杂度:
O(m2)
【代码】
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N=101;
const int M=21;
const int MAX=100000;
int n,m,k,e;
struct G
{
int v,d,nxt;
}map[M*M<<1];
int tot,hd[M],cant[M][N];
int d[M],q[M],h,t,used[M];
int f[N],can[M];
inline void ins(int u,int v,int d)
{
map[++tot].v=v;
map[tot].d=d;
map[tot].nxt=hd[u];
hd[u]=tot;
}
void init(void)
{
int x,y,z;
scanf("%d%d%d%d",&n,&m,&k,&e);
for (int i=1;i<=e;i++)
{
scanf("%d%d%d",&x,&y,&z);
ins(x,y,z),ins(y,x,z);
}
scanf("%d",&e);
for (int i=1;i<=e;i++)
{
scanf("%d%d%d",&x,&y,&z);
for (int j=y;j<=z;j++) cant[x][j]=1;
}
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
cant[i][j]+=cant[i][j-1];
}
inline int min(int i,int j)
{
return i<j?i:j;
}
int SPFA(void)
{
memset(used,0,sizeof used);
for (int i=1;i<=m;i++) d[i]=MAX;
h=0,t=1,d[1]=0,q[t]=1,used[1]=1;
int k;
for (;h^t;)
{
k=q[h=h%m+1];
for (int kk=hd[k];kk;kk=map[kk].nxt)
if (can[map[kk].v]&&d[k]+map[kk].d<d[map[kk].v])
{
d[map[kk].v]=d[k]+map[kk].d;
if (!used[map[kk].v]) used[q[t=t%m+1]=map[kk].v]=1;
}
used[k]=0;
}
return d[m];
}
void work(void)
{
f[0]=-k;
for (int i=1;i<=n;i++) f[i]=MAX;
for (int i=1;i<=n;i++)
for (int j=0;j<i;j++)
{
for (int kk=1;kk<=m;kk++)
can[kk]=!(cant[kk][i]-cant[kk][j]);
f[i]=min(f[i],f[j]+SPFA()*(i-j)+k);
}
printf("%d\n",f[n]);
}
int main(void)
{
init();
work();
return 0;
}
【小结】求 N <script type="math/tex" id="MathJax-Element-423">N</script>天最小的,可以通过递推或者直接求的方法。