2020杭电多校第四场 1004 Deliver the Cake

题目

在这里插入图片描述
题目链接

题目大意是:
给定一张无向有权图,已知其起点和终点,每个节点有方向LR或M。蛋糕每次换手需要一定时间x。在方向为L或R的节点,蛋糕必须在对应的手上,而方向为M的地方则无所谓。

问最终从起点到终点的最短路程是多少。

拆点+最短路

这道题,一步一步的来分析

第一步,如果没有对点的方向限制,将会是一个非常简单的最短路问题。现在有了方向限制。

第二步,假设当前仅有左右两种方向的限制。那么问题也会变得简单只需要将两端方向不一样的边权加上x,这样就没有了方向限制,跑第一步即可。

第三步,考虑到存在方向为M的点,可以将其看作是两个点,一个是方向为左的点一个是方向为右的点,将其拆开,与周围的点重新建边。

在这里插入图片描述
经过这样一顿操作,图中就没有方向为M的点了,剩下的图就只剩下方向为左或者右的点了,按照刚刚第二步分析的那样实现就好。
在这里插入图片描述

需要注意的是,这里拆点可能会造成边的数量很多,建议开10被的m。

另外,起点和终点有可能被拆成两个顶点,为了不跑多次最短路,可以建造一个虚拟起点和终点分别向拆开的起点和终点连接长度为0的边。

代码:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int M = 1000060;
const int N = 1000060;
struct E{
    int point;
    int next;
    long long dis; 
}nxt[M];
struct Node{
    int po;
    long long dis;
    
    bool operator < (const Node& o)const{
        return dis > o.dis;
    }
};

priority_queue<Node> q;
int head[N];
int dir[N];
int T,n,m,s1,s2,t1,t2,x,tot,s,t;
char dirC[N];
bool vis[N];

void link(int a,int b,long long dis){
    nxt[++tot] = {b,head[a],dis};
    head[a] = tot;
}

void dfs1(int k){
    vis[k] = true;
    if(dir[k] == 0){
        n++;
        vis[n] = true;
        dir[k] = -1;
        dir[n] = 1;
        
        if(k == s1)s2 = n;
        if(k == t1)t2 = n;
        
        for(int i = head[k],j;i;i = nxt[i].next){
            j = nxt[i].point;
            link(j,n,nxt[i].dis);
            link(n,j,nxt[i].dis);
        }
    }
    for(int i = head[k],j;i;i = nxt[i].next){
        if(vis[nxt[i].point])
            continue;
        dfs1(nxt[i].point);
    }
}

void dfs2(int k){
    vis[k] = true;
    for(int i = head[k];i;i = nxt[i].next){
        nxt[i].dis += (dir[k] != dir[nxt[i].point]) * x;
    }
    for(int i = head[k];i;i = nxt[i].next){
        if(vis[nxt[i].point])
            continue;
        dfs2(nxt[i].point); 
    }
}

void clearVis(){
    for(int i = 1;i <= n;i++){
        vis[i] = false;
    }
}

long long dijkstar(){
    clearVis();
    q.push({s,0});
    
    Node o;
    while(!q.empty()){
        o = q.top();
        q.pop();
        if(vis[o.po]) continue;
        
        if(o.po == t) return o.dis;
        
        vis[o.po] = true;
        
        for(int i = head[o.po],j;i;i = nxt[i].next){
            j = nxt[i].point;
            if(vis[j])    continue;
            q.push({j,o.dis + nxt[i].dis});
        }
    }
}

int main(){
    for(cin >> T;T;T--){
        tot = 0;s2 = t2 = 0; 
        t = s = 0;
        while(!q.empty()) q.pop();
        scanf("%d%d%d%d%d",&n,&m,&s1,&t1,&x);
        scanf("%s",dirC + 1);
        
        for(int i = 1;i <= n;i++){
            
            if(dirC[i] == 'R')
                dir[i] = 1;
            if(dirC[i] == 'L')
                dir[i] = -1;
            if(dirC[i] == 'M')
                dir[i] = 0;
        }
        
        for(int i = 1;i <= n << 1;i++)    head[i] = 0;
            
        for(int i = 1,a,b,dis;i <= m;i++){
            scanf("%d%d%d",&a,&b,&dis);
            link(a,b,dis);
            link(b,a,dis);
        }
        clearVis();
        dfs1(s1);
        s = ++n;t = ++n;
        
        clearVis();
        dfs2(s1);
        if(s2)    dfs2(s2);
                
        link(s,s1,0);
        if(s2)    link(s,s2,0);
        link(t1,t,0);
        if(t2)    link(t2,t,0);
        cout << dijkstar() << endl;
        
    }
}
“规范化示例”的概念起源于敏捷软件开发方法论,它是一种基于实例和集体理解的软件规范方法。成功的团队通过规范化示例交付正确的软件。 规范化示例的核心思想是通过一系列实际案例来定义软件的功能需求和行为。这些案例是从真实业务场景中提取的,描述了用户和系统之间的交互,以及期望的结果。团队成员通过与利益相关者一起讨论、验证和澄清这些示例,共同理解和定义软件的要求。 规范化示例的成功之处在于有效地传递信息。与传统的需求文档不同,规范化示例提供了直观、具体和可测量的例子,帮助团队和利益相关者更好地理解软件所需的功能和行为。这种可视化的形式有助于避免语义歧义和“对理解的理解”,减少沟通误差,提高团队的协作效率。 另外,规范化示例还加强了团队的自组织能力。通过参与规范化示例的制定和验证,团队成员之间的合作与沟通得到加强,每个人都可以更好地理解软件的需求和目标。这种集体的参与和讨论有助于提高团队的专业素养,减少个人行为对软件交付的负面影响,并增强团队对软件需求变化的适应能力。 另外,规范化示例还有助于提高软件的质量。通过与规范化示例进行对比和验证,团队可以快速发现和解决潜在的问题和漏洞,确保软件的功能和行为符合用户的期望。这种迭代式的开发和测试方法可以有效降低软件交付过程中的风险,提高软件的稳定性和可靠性。 总之,规范化示例是一种成功团队交付正确软件的有效方法。它提供了直观、具体和可测量的需求和行为定义,促进团队协作和自组织能力,增强软件质量和交付效率。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值