喵呜的虚拟城市(最短路)

Problem C: 虚拟城市

Time Limit: 2 Sec  Memory Limit: 128 MB
Submit: 5  Solved: 2
[ Submit][ Status][ Web Board]

Description

虚拟城市是一个基于Windows平台的2D经营策略游戏。你可以在游戏中建造各种各样的建筑(公园、雕塑、喷泉、小屋、公寓、办公区、商场写字楼、综合办公楼、办公大楼、现代化摩天大楼等)、道路,也可以销毁他们, 当然建造、销毁它们都要花费资金,你可以通过每月的税收赚钱,就像专业的大型策略游戏一样。

在游戏中,公共运输系统很重要的,所以你需要在城市里建造车站,然后指派巴士在不同的车站之间往返。而且车站是有容量的,也就是说每个车站会有一个人数上限,如果想改变这个上限就需要升级或者降级车站。
         现在在游戏中,你已经建造了n个车站,其中1号车站位于体育场门口,n号车站位于剧场门口,还有一些巴士,会把一个车站的人带往另一个车站。注意这里的巴士都是单行的,即如果他负责把x号车站的乘客带往y号车站,他就不负责把y号车站的乘客带往x号车站。当然,如果x号车站的容量小于y号车站,这样y号车站的容量就得不到充分的利用,你并不想看到这种情况发生,所以就会停运这趟巴士。
          因此,现在人们想从体育场通过未停运的巴士到达剧场,在这之前,你可以通过对每个车站容量进行升级或者降级,使得重新规划后未停运的巴士可以将他们从体育场运输到剧场,当然,升级或降级都是有花费的,每次升级消耗1金币,将车站的容量上升1,降级也是一样的,每次消耗1金币,车站容量下降1,当然,你也不希望花费的金币太多,因此,你希望知道,最少花费多少金币能使得人们可以通过城市的公共交通系统从体育场到达剧场。

Input

第一行输入一个数T,表示测试数据个数,对于每组测试数据,第一行输入两个数n, m(2<=n<=50, 0<=m<=n*(n-1)),表示有n个车站,且这n个车站之间共存在m辆巴士。接下来,输入m行,每行两个数x, y(1<=x, y<=n),表示x和y之间有一辆巴士,且如果容量满足v(x)>=v(y)时,这辆巴士不会被停运,即可以将人们从x输送至y,但是无论容量怎么变,这辆车是不负责将人们从y输送至x。题目保证x和y不相同,也保证不会有两辆巴士有相同的起点和终点。接下来一行输入n个数,表示初始状态下n个车站的容量。(1<=v(i)<=10^9)

Output

对于每组测试数据,输出一个数,表示能让人们可以通过城市的公共交通系统从体育场到达剧场的最小花费,如果无论如何都到达不了,输出-1。

Sample Input

3
3 4
1 2
2 1
2 3
3 2
30 20 10
2 2
1 2
2 1
10 20
3 2
1 2
2 1
10 10 10

Sample Output

0
10
-1

HINT

Hint

第一组数据,人们可以直接通过1->3这辆巴士从体育场直达剧场,第1个车站的容量大于等于第3个车站的容量,因此这辆巴士是可以乘坐的,因此花费为0。

第二组数据,存在1->2的巴士,但是第1个车站的容量小于第2个车站的容量,因此这辆巴士是停运的,你可以把这两个车站的容量都变成15,这样花费的代价为|10-15|+|20-15|=10,最小。注意,巴士的起点和终点的高度相同时也是可以运行的。

第三组数据,因为没有可以到达剧场的巴士,因此输出-1。

分析:首先,最终的情形一定是这样,每个车站的容量一定为v(1)~v(n)中的一个,那么可以把一个车站分成n个点,然后根据v(x)>=v(y)连边构图。最后跑一遍最短路即可。
           构图: 加入一个超级源点0,源点0与起点车站1所分成的n个点相连,那么车站1形成的点即为1~n,形成的边即为0->i,边的权值即为abs(v(1)-v(i))。依此类推,对于车站x->y,如果v(i) >= v(j),那么连边 (x-1)*n+i -> (y-1)*n+j,权值为abs(v(y)-v(j))。
代码清单:
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn = 2500 + 5;
const int maxv = 200000 + 5;
const ll max_dis = 1e18 + 5;

int T;
int n,m,a,b;
int tmap[55][55];
int dist[55];
ll dis[maxn];

struct Edge{
    int to,dis;
    Edge(){}
    Edge(int to,int dis){
        this -> to = to;
        this -> dis = dis;
    }
};
vector<Edge>graph[maxn];

typedef pair<int,int>P;

void init(){
    memset(tmap,0,sizeof(tmap));
    for(int i=0;i<maxn;i++)
        graph[i].clear();
}

void input(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&a,&b);
        tmap[a][b]=1;
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&dist[i]);
}

//把一个点分成n个点,然后连边
void add(int u,int v){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dist[i]>=dist[j])
                graph[(u-1)*n+i].push_back(Edge((v-1)*n+j,abs(dist[j]-dist[v])));
        }
    }
}

void createGraph(){
    for(int i=1;i<=n;i++){
        graph[0].push_back(Edge(i,abs(dist[i]-dist[1])));
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(tmap[i][j]){
                add(i,j);
            }
        }
    }
}

ll dijkstra(int s){
    fill(dis,dis+maxn,max_dis);
    priority_queue<P,vector<P>,greater<P> >q;
    while(!q.empty()) q.pop();
    dis[s]=0;
    q.push(P(0,s));
    while(!q.empty()){
        P p=q.top();q.pop();
        int u=p.second;
        if(p.first>dis[u]) continue;
        for(int i=0;i<graph[u].size();i++){
            Edge& e=graph[u][i];
            if(dis[e.to]>dis[u]+e.dis){
                dis[e.to]=dis[u]+e.dis;
                q.push(P(dis[e.to],e.to));
            }
        }
    }
    ll ans=max_dis;
    //车站n所形成的点是(n-1)*n+1~n*n
    for(int i=(n-1)*n+1;i<=n*n;i++){
        ans=min(ans,dis[i]);
    }
    if(ans==max_dis) return -1;
    return ans;
}

void solve(){
    createGraph();
    printf("%lld\n",dijkstra(0));
}

int main(){
    //freopen("cin.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        init();
        input();
        solve();
    }return 0;
}



你可能感兴趣的:(Algorithm,ACM,priority_queue,dijkstra,Graphs)