bzoj 1003物流运输 区间dp+spfa
基本思路:
一开始确实没什么思路,因为觉得怎么着都会超时,然后看一下数据范围,呵,怎么都不会超时。
思路:
1.看到能改变线路,想到可以用以下区间dp,区间dp的话,先枚举长度,枚举开始位置,然后枚举中间点 dp[i][j]=min(dp[i][j],dp[i][z]+dp[z][j]+k);
2.然后每段时间最短路究竟是多少,然后因为不会超时,所以就二重循环枚举就好了
(ps:这里要说明一下给自己提个醒:
spfa就是队列优化的迪杰斯特拉,然后的话vis数组每次出一个就置零一个,如果不置零是错的
堆优化的迪杰斯特拉自己之前也加vis,而且每次出一个的话不置零,不会错,因为vis数组是不必要的
所以以后写的话一般就写spfa好了,堆优化的迪杰斯特拉的话略难写一点,而且两者速度也差不多
普通的迪杰斯特拉的话还是要置零的,因为比起spfa就是优化在了队列
)
代码如下:
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<string> #include<algorithm> #include<queue> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; const int maxn = 100+10; int head[maxn],cnt; struct Edge{ int to,val,next; }edge[maxn<<4]; ll tim[maxn][maxn],f[maxn][maxn]; bool flag[maxn][maxn],block[maxn],vis[maxn]; int dis[maxn]; int n,m,k; void add(int u,int v,int w){ edge[cnt].to=v; edge[cnt].val=w; edge[cnt].next=head[u]; head[u]=cnt++; } int spfa(int s,int e){ memset(block,false,sizeof(block)); memset(vis,false,sizeof(vis)); memset(dis,inf,sizeof(dis)); queue<int>q; q.push(1);vis[1]=true;dis[1]=0; for(int i=s;i<=e;i++){ for(int j=1;j<=m;j++){ if(flag[i][j]){ block[j]=true; } } } while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; int w=edge[i].val; if(!block[v]&&dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(!vis[v]){ vis[v]=true; q.push(v); } } } vis[u]=false; } return dis[m]; } void dp(){ for(int l=1;l<=n;l++){ for(int s=1;s<=n;s++){ int e=s+l-1; if(e>n) continue; f[s][e]=(ll)tim[s][e]*(e-s+1); for(int z=s;z<e;z++){ f[s][e]=min(f[s][e],f[s][z]+k+f[z+1][e]); } } } } int main(){ memset(head,-1,sizeof(head));cnt=0; int p; scanf("%d%d%d%d",&n,&m,&k,&p); for(int i=1;i<=p;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } int q; scanf("%d",&q); for(int i=1;i<=q;i++){ int id,u,v; scanf("%d%d%d",&id,&u,&v); for(int j=u;j<=v;j++){ flag[j][id]=true; } } for(int i=1;i<=n;i++){ for(int j=i;j<=n;j++){ tim[i][j]=spfa(i,j); } } dp(); cout<<f[1][n]<<endl; return 0; }