PAT 1018 Public Bike Management (30 分)(最短路+dfs)

题意

有一个公共自行车网络,现在你要为公共自行车管理中心(PBMC)查询一下路线,PBMC做为0号节点,除了0号节点外还有n个自行车站点 S i S_i Si,每个自行车站点的自行车容量为C,我们认为一个自行车站有 C 2 \frac{C}{2} 2C辆自行车时是perfect的,现在告诉你这n个站点每个站点的当前自行车数量,并且给你这个网络图的m条边,每条边代表两点之间所需要的时间,题目保证是从0出发的联通图,现在你要求你找到一条从PBMC出发到达 S p S_p Sp的路,并且你需要把这条路上的每一个自行车站点调整为perfect状态,你可以从PBMC送出一些自行车也可以把其中某些数量大于 C 2 \frac{C}{2} 2C的站点中的自行车送到下一个站点,不允许往回送,若最后调整完 S p S_p Sp后还有多余的自行车,还要送回PBMC。对于这条路的要求是时间最短,若有多条最短路,求从PBMC送出最少数量自行车的路,若sent出去的自行车数量还有相同的要求back回来的自行车数量最小的路,题目保证这样的路唯一

思路

首先要求时间最小,那么先跑一遍Dij求最短路所需要的时间,然后剩下的再用dfs去遍历所有到达 S p S_p Sp时间为 l o w c o s t [ S p ] lowcost[S_p] lowcost[Sp]的路,对于每一个路,我们记录下他经过了哪些节点,再计算一下经过这条路,PBMC需要sent多少辆自行车,需要back多少辆自行车,由于调整路上节点的过程是不可逆的,所以一个for可以求出sent和back,记录当前拿走的和补上的数量就可以了,然后记录整个dfs过程中sent最小时back最小的就行了

#include <bits/stdc++.h>
using namespace std;
const int N=505;
const int INF=0x3f3f3f3f;
int cost[N][N];
int lowcost[N];
bool vis[N];
int c[N];
int pre[N];
int ans[N];
int sent=INF;
int back=INF;
int low;
int cnt;
int num;
int C,n,Sp,m;
void Dijstra(int beg)
{
    for(int i=0; i<=n; i++)
    {
        lowcost[i]=INF;
        vis[i]=0;
    }
    lowcost[beg]=0;
    for(int j=0; j<=n; j++)
    {
        int k=-1;
        int minn=INF;
        for(int i=0; i<=n; i++)
        {
            if(!vis[i]&&minn>lowcost[i])
            {
                minn=lowcost[i];
                k=i;
            }
        }
        if(k==-1) break;
        vis[k]=1;
        for(int i=0; i<=n; i++)
        {
            if(!vis[i]&&lowcost[i]>lowcost[k]+cost[k][i])
            {
                lowcost[i]=lowcost[k]+cost[k][i];
            }
        }
    }
}
void dfs(int now,int x)
{
    if(x==Sp)
    {
        if(now==low)
        {
            int s=0;
            int h=0;
            for(int i=0; i<cnt; i++)
            {
                if(c[pre[i]]<C&&h==0)
                {
                    s+=C-c[pre[i]];
                }
                else if(c[pre[i]]<C&&h!=0)
                {
                    if(C-c[pre[i]]<=h)
                    {
                        h-=C-c[pre[i]];
                    }
                    else
                    {
                        s+=C-c[pre[i]]-h;
                        h=0;
                    }
                }
                else if(c[pre[i]]>C)
                {
                    h+=c[pre[i]]-C;
                }
            }
            if(s<sent)
            {
                sent=s;
                back=h;
                num=cnt;
                for(int i=0; i<cnt; i++)
                    ans[i]=pre[i];
            }
            else if(s==sent)
            {
                if(back>h)
                {
                    back=h;
                    num=cnt;
                    for(int i=0; i<cnt; i++)
                        ans[i]=pre[i];
                }
            }
        }
        return;
    }
    for(int i=0; i<=n; i++)
    {
        if(!vis[i]&&cost[x][i]!=INF)
        {
            vis[i]=1;
            pre[cnt++]=i;
            now+=cost[x][i];
            dfs(now,i);
            vis[i]=0;
            cnt--;
            now-=cost[x][i];
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&C,&n,&Sp,&m);
    C/=2;
    for(int i=1; i<=n; i++)
        scanf("%d",&c[i]);
    memset(cost,INF,sizeof(cost));
    for(int i=0; i<m; i++)
    {
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        cost[u][v]=cost[v][u]=c;
    }
    Dijstra(0);
    low=lowcost[Sp];
    memset(vis,0,sizeof(vis));
    vis[0]=1;
    dfs(0,0);
    printf("%d 0",sent);
    for(int i=0; i<num; i++)
        printf("->%d",ans[i]);
    printf(" %d\n",back);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值