bzoj 1003 物流運輸 (預處理最短路+區間dp)
阿新 • • 發佈:2018-12-15
題意:給一個圖,讓你跑n次最短路,第i次最短路有若干點是不能使用的,並且更改一次路線就又需要加k的額外花費,問跑完n次以後最小花費。
思路:切入點在於某個時間段內花費是不變的,更改路線要加花費,自然想到dp。所以想到把n次最短路分割成多個區間最短路的和,對於每種最短路,我們可以預處理,因為只有n*n的區間,n最大100。設dp[i][j]表示第i次到第j次最短路的所有花費的最小值。那麼答案就是dp[1][n]。
區間dp就是套路的寫法,第一維列舉區間長度,第二維列舉起點,第三維列舉中間點。
預處理一個時間段的最短路要檢查某個點是否可用,可用才能加入鬆弛。
(n和m傻傻分不清楚,多wa了幾發)
#include<bits/stdc++.h> using namespace std; const int maxn=200; const int maxm=1000; int cnt=-1,n,m,k,e; int head[maxn],use[110][110],dist[maxn],dp[110][110]; bool inq[maxn]; struct Edge { int nex; int to,w; } edge[maxm]; void add_edge(int u,int v,int w) { edge[++cnt].nex=head[u]; edge[cnt].to=v; edge[cnt].w=w; head[u]=cnt; } bool check(int x,int s,int t) { for(int i=s; i<=t; i++) if(!use[x][i]) return false; return true; } int spfa(int s,int t) { memset(dist,0x3f,sizeof(dist)); memset(inq,false,sizeof(inq)); queue<int>que; while(!que.empty()) que.pop(); que.push(1); dist[1]=0; inq[1]=true; while(!que.empty()) { int u=que.front(); que.pop(); inq[u]=false; for(int i=head[u]; i!=-1; i=edge[i].nex) { int v=edge[i].to; if(check(v,s,t)&&dist[v]>dist[u]+edge[i].w) { dist[v]=dist[u]+edge[i].w; if(!inq[v]) inq[v]=true,que.push(v); } } } return dist[m]==0x3f3f3f3f ? 0x3f3f3f3f:dist[m]*(t-s+1); } void solve() { for(int i=1; i<=n; i++) { for(int j=i; j<=n; j++) dp[i][j]=spfa(i,j); } int e; for(int len=2; len<=n; len++) for(int s=1; (e=s+len-1)<=n; s++) for(int p=s; p<s+len-1; p++) { dp[s][e]=min(dp[s][e],dp[s][p]+dp[p+1][e]+k); } printf("%d\n",dp[1][n]); } int main() { while(~scanf("%d %d %d %d",&n,&m,&k,&e)) { memset(head,-1,sizeof(head)); cnt=-1; for(int i=1;i<maxm;i++) edge[i].nex=-1; for(int i=1; i<=e; i++) { int a,b,c; scanf("%d %d %d",&a,&b,&c); add_edge(a,b,c); add_edge(b,a,c); } memset(use,1,sizeof(use)); int tem; scanf("%d",&tem); for(int i=1; i<=tem; i++) { int p,a,b; scanf("%d %d %d",&p,&a,&b); for(int j=a; j<=b; j++) use[p][j]=0; } solve(); } }