1. 程式人生 > >bzoj 1003 物流運輸 (預處理最短路+區間dp)

bzoj 1003 物流運輸 (預處理最短路+區間dp)

題意:給一個圖,讓你跑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();
    }
}