[BZOJ3185][Coci2011][DP]kamion

本文介绍了一种使用动态规划解决火车路径规划问题的方法。通过定义状态f(i,j,k)为从i到j恰好用了k步并且到j时火车为空的方案数,引入辅助函数g(i,j,k)和h(i,j,k)来优化转移过程,最终实现时间复杂度为O(n^4)的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

考虑转化一下问题
f(i,j,k)表示从i到j恰好用了k步,并且到j的时候火车厢为空的方案数。

那么转移就是 f(i,j,k)=f(a,b,k1)f(c,j,k2),转移成立当且仅当存在i->a的边,载上货物x;b->c的边卸下货物x,k1+k2+2=k

但是这个DP时间复杂度是O(E2k2),这个时候要引入一个新的函数g(i,j,k),表示从i到j恰好用了k步,到j时车厢为空,且中途车厢从来没空过

那么转移就变成f(i,j,k)=g(i,a,k1)f(a,j,kk1)+f(b,j,k1)
g(i,j,k)=f(a,b,k2),其中存在i->a的边在上货物x,b->j的边卸下货物x,存在i->b的路径不需要载上或卸下货物。

这样每次转移就优化到O(n4)

那至于怎么求出原题的答案,原题解很抠,没有给出…

需要再引入一个函数h(i,j,k),表示从i到j用了k步,但是车厢不一定为空的方案数…
转移还是h(i,j,k)=g(i,a,k1)h(a,j,kk1)+h(b,j,k1)
因为神奇的g函数,这样转移的正确也就保证了……

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#define N 55
#define P 10007
#define foc(x,a) for(ITR x=a.begin();x!=a.end();x++)

using namespace std;

typedef pair<int,int> parii;
typedef vector<parii>::iterator ITR;

int n,m,K,Ans;
char A[100];
int f[N][N][N][2],g[N][N][N];
vector<parii> load[N][30],unload[N][30];
vector<parii> road[N],iroad[N];

inline void Read(){
  gets(A);
  int x,y,cnt; char c;
  cnt=sscanf(A,"%d %d %c",&x,&y,&c);
  if(cnt==2){
    road[x].push_back(parii(x,y));
    iroad[x].push_back(parii(x,y));
  }
  else if(c>='A'&&c<='Z'){
    load[x][c-'A'].push_back(parii(x,y));
    iroad[x].push_back(parii(x,y));
  }
  else unload[y][c-'a'].push_back(parii(x,y));

}

int main(){
  scanf("%d%d%d",&n,&m,&K); gets(A);
  for(int i=1;i<=m;i++) Read();
  for(int i=1;i<=n;i++) f[i][i][0][0]=f[i][i][0][1]=1;
  for(int k=1;k<=K;k++){
    if(k>=2)
      for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      for(int t=0;t<26;t++)
        foc(x,load[i][t]) foc(y,unload[j][t])
          (g[i][j][k]+=f[x->second][y->first][k-2][0])%=P;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
    for(int s=0;s<2;s++){
      if(s) foc(x,iroad[i]) (f[i][j][k][s]+=f[x->second][j][k-1][s])%=P;
      else foc(x,road[i]) (f[i][j][k][s]+=f[x->second][j][k-1][s])%=P;
    }
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
    for(int s=0;s<2;s++)
      for(int x=1;x<=n;x++)
        for(int t=2;t<=k;t++)
          (f[i][j][k][s]+=g[i][x][t]*f[x][j][k-t][s])%=P;
    (Ans+=f[1][n][k][1])%=P;
  }
  return printf("%d\n",Ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值