uva658(最短路径+隐式图+状态压缩)

本文探讨了如何通过补丁应用的优化顺序,解决软件bug修复问题。利用隐式图搜索技术结合Dijkstra算法,实现了在大量可能状态中找到修复所有bug所需的最短时间。

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

题目连接(vj):https://vjudge.net/problem/UVA-658

题意:补丁在修正 bug 时,有时也会引入新的 bug。假定有 n(n≤20)个潜在 bug 和 m(m≤100) 个补丁,每个补丁用两个长度为 n 的字符串表示,其中字符串的每个位置表示一个 bug。第一 个串表示打补丁之前的状态(“-” 表示该 bug 必须不存在,“+” 表示必须存在,0 表示无所 谓),第二个串表示打补丁之后的状态(“-” 表示不存在,“+” 表示存在,0 表示不变)。每 个补丁都有一个执行时间,你的任务是用最少的时间把一个所有 bug 都存在的软件通过打补 丁的方式变得没有 bug。一个补丁可以打多次。 在任意时刻,每个 bug 可能存在也可能不存在,所以可以用一个 n 位二进制串表示当前软 件的 “状态”。打完补丁之后,bug 状态会发生改变,对应 “状态转移”。是不是很像动态规 划?可惜动态规划是行不通的,因为状态经过多次转移之后可能会回到以前的状态,即状态 图并不是 DAG。如果直接用记忆化搜索,会出现无限递归。 正确的方法是把状态看成结点,状态转移看成边,转化成图论中的最短路径问题,然后 使用 Dijkstra 或 Bellman-Ford 算法求解。不过这道题和普通的最短路径问题不一样:结点很 多,多达 2 n 个,而且很多状态根本遇不到(即不管怎么打补丁,也不可能打成那个状态), 所以没有必要像前面那样先把图储存好。 还记得第 7 章中介绍的 “隐式图搜索” 吗?这里也可以用相同的方法:当需要得到某个结 点 u 出发的所有边时,不是去读 G[u],而是直接枚举所有 m 个补丁,看看是否能打得上。不管 是 Dijsktra 算法还是 Bellman-Ford 算法,这个方法都适用。本题很经典,强烈建议读者编程实现。 

以上题意以及思路来自紫书

解题思路:抽象成最短路问题,因为状态(节点)太多,而且很多不会出现,所以不能单独建完图再跑最短路,所以采用隐式图,每次扩展遍历一遍已知补丁,匹配成功则看是否可以松弛,记录松弛后的状态。

当前状态唯一确定,所以压缩为一个n位二进制串,0表示无bug,1表示有,而补丁的状态不唯一确定,所以用两个二进制串表示,has串和no串,has串1表示该位置一定有,no串1表示该位置一定无。

匹配方法:cur表示当前状态。如果 cur&no==0并且cur|yes=cur 则匹配成功,匹配成功后松弛后的状态为 v=(u|p.to_has)&(~p.to_no)。然后跑dijkstra即可。

代码如下:

#include<bits/stdc++.h>
#define MAX (1<<20)+10
#define INF 0x3fffffff
using namespace std;

struct data
{
    int from_no,from_has,to_no,to_has,dist;
} patch[105];

struct heapnode
{
    int d,u;
    bool operator < (const heapnode& rhs) const
    {
        return d>rhs.d;
    }
};

int l,m;
bool vis[MAX];
long long d[MAX];

void init(void)
{
    for(int i=0; i<m; i++)
    {
        patch[i].from_has=0;
        patch[i].from_no=0;
        patch[i].to_has=0;
        patch[i].to_no=0;
    }
    memset(vis,0,sizeof(vis));
    for(int i=0; i<MAX; i++)
        d[i]=INF;
}

bool dijkstra(void)
{
    priority_queue<heapnode> Q;
    int s=(1<<l)-1;
    d[s]=0;;
    Q.push((heapnode){0,s});
    while(!Q.empty())
    {
        heapnode x=Q.top();
        Q.pop();
        int u=x.u;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=0; i<m; i++)
        {
            data &p=patch[i];
            if(!(u&p.from_no)&&((u|p.from_has)==u))//匹配成功
            {
                int v=(u|p.to_has)&(~p.to_no);
                if(d[v]>d[u]+p.dist)
                {
                    d[v]=d[u]+p.dist;
                    Q.push((heapnode){d[v],v});
                }
            }
        }
    }
    if(d[0]==INF)
        return 0;
    return 1;
}

int main()
{
    int T=0;
    while(~scanf("%d%d",&l,&m)&&l)
    {
        init();
        for(int i=0; i<m; i++)
        {
            char from[25],to[25];
            int dis;
            scanf("%d%s%s",&dis,from,to);
            patch[i].dist=dis;
            for(int j=0; j<l; j++)
            {
                if(from[j]=='+') patch[i].from_has=patch[i].from_has|(1<<j);
                if(from[j]=='-') patch[i].from_no=patch[i].from_no|(1<<j);
                if(to[j]=='+') patch[i].to_has=patch[i].to_has|(1<<j);
                if(to[j]=='-') patch[i].to_no=patch[i].to_no|(1<<j);
            }
        }
        printf("Product %d\n",++T);
        if(dijkstra())
            printf("Fastest sequence takes %d seconds.\n",d[0]);
        else
            printf("Bugs cannot be fixed.\n");
        printf("\n");
    }
}

 

转载于:https://www.cnblogs.com/cryingrain/p/8934181.html

<think>好的,我现在需要回答用户关于MySQL中显式锁和隐式锁的区别及用法的问题。首先,我得回忆一下自己之前学过的相关知识,确保信息的准确性。 首先,显式锁和隐式锁的定义。显式锁是由用户或SQL语句明确请求的,比如使用SELECT ... FOR UPDATE或者LOCK TABLES这样的语句。而隐式锁是数据库引擎自动加上的,比如在插入操作时,为了保证事务的隔离性,InnoDB会自动加锁,不需要用户干预。这一点我记得引用里的内容提到过,隐式锁由MySQL内部处理,不需要手动管理[^1]。 接下来,区别方面。用户需要知道两者的控制方式、使用场景、锁类型以及可见性。显式锁是用户主动控制的,比如事务中需要更新数据前先锁定,防止其他事务修改。而隐式锁是自动的,比如插入数据时,引擎自动加锁防止并发问题。锁类型的话,显式锁可能包括行锁、表锁、间隙锁等,而隐式锁通常是行级的。可见性方面,显式锁可以通过performance_schema查看,而隐式锁在转换前可能不可见,但引用里提到在某些情况下隐式锁会转为显式锁,这时就能查到了[^2]。 然后使用场景。显式锁适合需要明确控制并发的情况,比如复杂的业务逻辑或避免死锁。隐式锁则用于常规操作,如普通的INSERT、UPDATE,由系统自动处理。这里要注意用户提到的快照读和当前读的区别,显式锁通常用于当前读,比如SELECT ... FOR UPDATE,而快照读可能依赖MVCC,不需要加锁[^3]。 还需要考虑事务类型。显式锁通常在显式事务中使用,比如用START TRANSACTION明确开始的事务。隐式锁可能在隐式事务中,比如自动提交模式下的单条语句。这点引用里提到显式锁通常用LOCK TABLES或者SELECT ... FOR UPDATE,而隐式锁自动处理[^3]。 另外,要提到两者的转换,比如当隐式锁遇到冲突时,可能会升级为显式锁,这样用户就能在performance_schema中看到了。这部分需要结合引用中的内容[^2]。 最后,总结区别的对比表格,帮助用户一目了然。同时生成相关问题,比如如何监控隐式锁,显式锁的常见命令,隐式锁如何避免死锁等,这些都需要根据之前的内容来设计。 检查是否有遗漏的点,比如锁的兼容性、不同隔离级别下的表现,但用户的问题主要区别和使用场景,可能不需要深入细节。不过要确保提到的点准确,比如显式锁的使用命令和隐式锁的自动管理,以及引用对应的来源。 现在把这些内容组织成结构清晰的回答,分点说明,并确保LaTeX格式正确,比如行内公式用$...$,独立公式用$$...$$,但当前问题可能不需要数学公式,所以重点在格式和引用正确。</think>### MySQL显式锁隐式锁的区别及用法 #### 一、核心区别 1. **控制方式** - **显式锁**:由用户通过SQL语句(如`SELECT ... FOR UPDATE`、`LOCK TABLES`)或事务命令主动声明。 - **隐式锁**:由MySQL引擎自动管理,例如插入数据时自动对索引加锁,无需用户干预[^2]。 2. **使用场景** - **显式锁**:适用于需要强制控制并发访问的场景,如避免脏读、幻读或实现悲观锁机制。 - **隐式锁**:用于常规数据操作(如`INSERT`、普通`UPDATE`),由InnoDB保证事务隔离性。 3. **锁类型可见性** - **显式锁**:可通过`performance_schema.data_locks`直接查询,支持表锁、行锁、间隙锁等。 - **隐式锁**:默认不可见,仅在发生冲突时可能转换为显式锁(如二级索引插入时触发锁升级)。 4. **事务关联性** - **显式锁**:需在显式事务(`START TRANSACTION`)中配合使用,例如`SELECT ... FOR UPDATE`锁定读取行[^3]。 - **隐式锁**:自动绑定到隐式事务(如自动提交模式下的单条语句)。 #### 二、典型用法对比 | **特性** | 显式锁 | 隐式锁 | |------------------|---------------------------------|---------------------------------| | **控制权** | 用户主动声明 | 引擎自动管理 | | **可见性** | 可直接监控 | 冲突时转为显式锁才可见 | | **锁粒度** | 支持表级、行级、间隙锁 | 主要为行级锁 | | **典型语句** | `LOCK TABLES`, `FOR UPDATE` | `INSERT`, 普通`UPDATE` | | **事务要求** | 需显式事务支持 | 隐式/显式事务均可 | #### 三、应用示例 1. **显式锁** ```sql START TRANSACTION; SELECT * FROM orders WHERE id = 100 FOR UPDATE; -- 显式锁定行 UPDATE orders SET amount = 200 WHERE id = 100; COMMIT; ``` *用途*:确保其他事务无法修改`id=100`的行,直到当前事务提交。 2. **隐式锁** ```sql INSERT INTO products (id, name) VALUES (101, 'Phone'); -- InnoDB自动对id=101加隐式锁 ``` *用途*:防止其他事务同时插入相同主键,保证唯一性。 #### 四、注意事项 1. **隐式锁的转换** 当隐式锁显式锁冲突时(如另一个事务尝试修改被隐式锁定的行),InnoDB会将其转换为显式锁,此时可通过`performance_schema`查看[^2]。 2. **死锁风险** 显式锁需谨慎设计加锁顺序,隐式锁因自动管理通常更安全,但批量操作仍可能引发锁竞争。 3. **隔离级别影响** 在`READ COMMITTED`级别下,显式锁可能仅锁定现有行;在`REPEATABLE READ`级别下,可能附加间隙锁防止幻读[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值