codeforces 983C(dp or shortest path)

本文深入探讨了一种电梯调度算法的设计与实现。通过定义状态d[i][j][s],代表第i层楼的情况,当前在第j层,电梯内乘客的目的地及电梯状态。采用Dijkstra算法,并分析了状态之间的转换,确保了算法的有效性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般而言规划题目侧重于思路,shortest path 也和规划一样需要建边,规划需要dag图,最短路只需要构成图即可,然后上单源最短路算法即可,实现上可能比较复杂。

例如本题目而言,首先看状态设计,d[i][j][s],代表前i个已经上电梯或已经到达,当前在i层,电梯内人员情况(空位和要到达楼层)

每个状态都可以往任意楼层走,然后上下乘客,但状态之间可能来回走来走去,可用dijstra算法。

进一步分析,当前只能最多只能去5个楼层,即电梯内乘客想去的楼层,或者下一位乘客起始楼层。到这五个楼层必然会有乘客的上下,这样便是DAG图(简单分析一下,不会走到原来的状态,便是DAG图)

那么S可能有多少种呐?

   int cnt = 0;
   for(int i= 0 ;i<=9;i++)
     for(int j=i;j<=9;j++)
        for(int k=j;k<=9;k++)
           for(int g =k;g<=9;g++)
               cout<<i<<j<<k<<g<<endl , cnt++;

cnt值为715,总共的状态715*10 * 2000为 1400万左右,单实际用到的状态很少,不是所有状态都可达到,实际使用状态在百万左右,这样乘上状态转移代价为5实际为百万级算法代价,加上使用map大概千万级的计算量


参考别人的算法,也有使用只记录前三个上电梯的人,因为第四个人只要上了,电梯我们只能选择去送其中一个人。这样电梯内总不会多于3个人。

对于自己的代码,使用map,要十分注意的是,对任意一个状态,开始要先上下乘客(因为这样做总是合算的),对原来的s要先保存下来,因为这个WRONG了两次

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#include<string>
using namespace std;

const int inf = 21474836;
const int maxn =2005;
const int F = 10;
map<string,int>d[maxn][10];
int start[maxn] , dest[maxn];
int n;
int dp(int i,int j,string s){
    if(d[i][j].count(s)) return d[i][j][s];
    d[i][j][s] = inf;
    string os = s;
    for(int k = 0; k<4;k++){
          int flo = s[k] -'0';
          if(flo == j) s[k]='0';
    }
    int pos = i + 1;
    for(int k=0;k<4;k++){
        int flo = s[k] -'0';
        if(pos <= n && flo == 0 && start[pos] == j)  s[k] = '0' + dest[pos] , ++pos;
    }
    if(i == n && s == (string)"0000"){
          return d[i][j][os] = 0;
    }
    sort(s.begin(),s.end());
    for(int k = 3 ; k>=0 ; k--){
         if(s[k] -'0'!= 0)
         d[i][j][os] = min(d[i][j][os] , dp(pos - 1, s[k]-'0',s)+abs(j - (s[k]-'0')));
         else if(pos - 1< n){
            d[i][j][os] = min(d[i][j][os] , dp(pos - 1, start[pos],s) + abs(j - start[pos]));
            break;
         }
    }
    return d[i][j][os];
}
int main()
{
   scanf("%d",&n);
   for(int i=1;i<=n;i++){
       scanf("%d %d",&start[i],&dest[i]);
   }
   cout<<dp(0,1,"0000") + 2 *n ;
   return 0;
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值