pat1026Table Tennis (30)

乒乓球场调度算法
本文介绍了一个涉及乒乓球台调度的问题,包括VIP与普通顾客在乒乓球台使用上的优先级处理方案,通过队列和特殊规则实现最优分配。

题意分析:

(1)给出N个人到达乒乓球场的时间以及他们需要打多长时间的球,这些人分为两类:一类是VIP客户,一类是非VIP客户,所有的客户按到来的时间排好队伍等候,一旦出现空的乒乓球台,客户将立即选择编号最小的乒乓球台,每个客户最多不能超过两个小时,求每个客户的等待时间,以及每个乒乓球台一天服务了多少人。

(2)VIP客户与普通客户不同点在于:一旦有VIP桌子空闲,处于等待队列中的最早到达的VIP客户(如果有的话)可以获得使用权,而不管他前面是否有人在等待。

(3)如果没有VIP客户,当VIP卓空闲时,普通客户也可使用。

(4)综上所述,需要维护三个队列:VIP客户到达时间的序列、普通用户到达时间的序列、每个桌子空闲的时刻,其中桌子的空闲时刻的计算跟1017题银行窗口空闲时刻的计算一样,大家可以去参考。

(5)这三个队列的更新策略是:一旦有桌子空闲,在队列中最早到达的人可以选择序号最小的空闲的桌子使用;有以下情况例外:若此桌是VIP桌子,且队列中有VIP等待客户,那么无论他前面是否还有等待的人,立即获得使用权;还有若既有普通桌子和VIP桌子空闲,VIP客户选择VIP桌子

可能坑点:

这题难倒了不少人,题干中有几处没有说明白(大坑!!!),其中也是巨坑连连。。。,总结起来有以下几点需要注意:

(1)每个乒乓球台的服务时间在8:00~21:00之间,超过21:00的不再服务
(2)等待队列中,VIP的优先级体现在当有空VIP专用球台时,VIP客户可以直接使用,而不管前面是否有普通客户在等待
(3)最为重要的一点,当有普通球台和VIP球台时,VIP客户遵循选择最小编号的VIP球台(VIP就要这么任性),而非编号最小的球台;普通客户遵循选择最小的球台
(4)每个人的服务时间在2小时之内,超过2小时的按2小时处理;
(5)注意最后计算等待时间时采用的是四舍五入

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <limits.h>
using namespace std;

struct players
{
    int arriveTime;
    int startServe;
    int serveTime;
    int waitTime;
    int isVIP;
};
//等待队列中的VIP客户
vector<players >vipPlayers;
//普通客户
vector<players >ordinaryPlayers;
//处理完输出信息的客户
vector<players >total;
struct tables
{
    int VIP;
    int cnt;
    int vacantTime;
};
tables t[101];

bool cmpPlayers(players a,players b)
{
    return a.arriveTime<b.arriveTime;
}

bool cmpStartServe(players a,players b)
{
    return a.startServe<b.startServe;
}

int main()
{
    int N,K,M,i=0;
    cin>>N;
    int hh,mm,ss,wait,vip;
    players temp;
    while(i<N)
    {
        scanf("%d:%d:%d %d %d",&hh,&mm,&ss,&wait,&vip);
        temp.arriveTime=hh*3600+mm*60+ss;
        temp.serveTime=wait*60;
        temp.isVIP=vip;
        i++;
        if(temp.arriveTime>=3600*21)continue;
        if(vip==1)vipPlayers.push_back(temp);
        else ordinaryPlayers.push_back(temp);
    }
    cin>>K>>M;
    for(int j=1;j<=K;j++)
    {
        t[j].VIP=0;
        t[j].vacantTime=8*3600;
        t[j].cnt=0;
    }
    int tag;
    for(int j=0;j<M;j++)
    {
        cin>>tag;
        t[tag].VIP=1;
    }

    if(ordinaryPlayers.size()>0)sort(ordinaryPlayers.begin(),ordinaryPlayers.end(),cmpPlayers);
    if(vipPlayers.size()>0)sort(vipPlayers.begin(),vipPlayers.end(),cmpPlayers);

    while(ordinaryPlayers.size()>0||vipPlayers.size()>0)
    {
        int minVacant=INT_MAX;
        int minVipVacant=INT_MAX;
        int index;
        int vipIndex;
        for(int j=1;j<=K;j++)
        {
            if(t[j].vacantTime<minVacant)
            {
                minVacant=t[j].vacantTime;
                index=j;
            }
            if(t[j].VIP==1&&t[j].vacantTime<minVipVacant)
            {
                minVipVacant=t[j].vacantTime;
                vipIndex=j;
            }
        }
        if(t[index].vacantTime>=21*3600)break;
        players nextPlayers;
        if(vipPlayers.size()==0)
        {
            nextPlayers=ordinaryPlayers.front();
            ordinaryPlayers.erase(ordinaryPlayers.begin());
        }
        else if(ordinaryPlayers.size()==0)
        {
            nextPlayers=vipPlayers.front();
            vipPlayers.erase(vipPlayers.begin());
        }
        else
        {
            if(t[index].VIP==1)
            {
                if(vipPlayers.front().arriveTime<ordinaryPlayers.front().arriveTime||t[index].vacantTime>=vipPlayers.front().arriveTime)
                {
                    nextPlayers=vipPlayers.front();
                    vipPlayers.erase(vipPlayers.begin());
                }
                else
                {
                    nextPlayers=ordinaryPlayers.front();
                    ordinaryPlayers.erase(ordinaryPlayers.begin());
                }
            }
            else
            {
                if(vipPlayers.front().arriveTime<ordinaryPlayers.front().arriveTime)
                {
                    nextPlayers=vipPlayers.front();
                    vipPlayers.erase(vipPlayers.begin());
                }
                else
                {
                    nextPlayers=ordinaryPlayers.front();
                    ordinaryPlayers.erase(ordinaryPlayers.begin());
                }
            }
        }
        //控制每个人的时间在两小时之内,超过两小时按两个小时截断
        if(nextPlayers.serveTime>7200)nextPlayers.serveTime=7200;
        //这里需要记清楚:一旦有VIP桌子空闲,等待队列中的VIP客户就会选择VIP桌子,不管是否有普通桌子空闲
        if(nextPlayers.isVIP==1&&nextPlayers.arriveTime>=t[vipIndex].vacantTime)
        {
            nextPlayers.waitTime=0;
            nextPlayers.startServe=nextPlayers.arriveTime;
            t[vipIndex].vacantTime=nextPlayers.arriveTime+nextPlayers.serveTime;
            t[vipIndex].cnt++;
        }
        else
        {
            if(nextPlayers.arriveTime<=t[index].vacantTime)
            {
                nextPlayers.waitTime=t[index].vacantTime-nextPlayers.arriveTime;
                nextPlayers.startServe=t[index].vacantTime;
                t[index].vacantTime+=nextPlayers.serveTime;
            }
            else
            {
                nextPlayers.waitTime=0;
                nextPlayers.startServe=nextPlayers.arriveTime;
                t[index].vacantTime=nextPlayers.arriveTime+nextPlayers.serveTime;
            }
            t[index].cnt++;
        }
        total.push_back(nextPlayers);
    }
    sort(total.begin(),total.end(),cmpStartServe);
    for(int i=0;i<total.size();i++)
    {
        int waitMinutes=total[i].waitTime/60+(total[i].waitTime%60<30?0:1);
        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",total[i].arriveTime/3600,
               (total[i].arriveTime%3600)/60,total[i].arriveTime%60,
               total[i].startServe/3600,(total[i].startServe%3600)/60,
               total[i].startServe%60,waitMinutes);
    }
    int first=1;
    for(int i=1;i<=K;i++)
    {
        if(first)first=0;
        else cout<<" ";
        cout<<t[i].cnt;
    }
    return 0;
}


<think> 嗯,用户想了解PAT机制的具体细节。参考了之前的引用资料,特别是引用[4]提到PAT支持下的Linux新API如pgprot_writecombine,这应该是理解PAT应用的关键切入点。 用户可能正在研究Linux内存管理或驱动开发,需要深入理解硬件级缓存控制机制。从用户引用大页管理的详细程度看,ta应该具备相当的OS基础知识层级。 需要系统性地组织PAT信息:先从硬件背景切入,解释传统MTRR的局限性;再说明PAT如何通过页表项扩展解决细粒度控制问题;重点描述7种内存类型及其应用场景;最后结合Linux实现说明API用法。特别注意要区PAT与用户之前关注的大页机制差异——大页解决地址转换效率问题,PAT解决缓存属性控制问题。 值得强调PAT在实际开发中的价值:比如图形驱动使用write-combining提升帧缓冲写入性能,或DMA场景用uncached避免缓存一致性问题。用户引用中debugfs的调试接口也可以作为实践部补充。 用户可能隐含的需求是:如何在实际编码中配置PAT属性?需要准备具体代码片段展示pgprot_writecombine的使用流程。同时要提醒常见陷阱,比如错误配置uncached导致性能下降。 </think> 以下是关于PAT(Page Attribute Table)机制的详细介绍: --- ### **一、PAT机制概述** PAT是x86架构中用于**精细控制物理内存缓存属性**的硬件特性。它允许操作系统在页表项(PTE)中直接指定单页面的缓存策略,突破传统MTRR(Memory Type Range Registers)只能管理大范围内存区域的限制[^4]。核心价值在于: 1. **细粒度控制**:可为每个4KB页面单独设置缓存类型 2. **动态调整**:应用程序运行时动态修改内存属性 3. **兼容扩展**:与页机制协同工作 --- ### **二、PAT工作原理** #### **1. 硬件支持** - CPU通过`CPUID`指令报告PAT支持 - 控制寄存器`CR4.PAT`位启用功能 - 扩展页表项结构: ```plaintext | 63-62 | 61-59 | ... | 12-7 | 6 | 5 | 4-3 | 2 | 1 | 0 | |-------|-------|-----|------|-----|-----|-----|-----|-----|-----| | NX | Ign | ... | PFN | PAT | D | PWT | PCD | R/W | P | ``` 关键位:`PAT`(Page Attribute Table)位扩展缓存类型控制 #### **2. 缓存类型编码** PAT结合`PAT/PCD/PWT`三位形成8种组合(实际使用7种): | PAT位 | PCD位 | PWT位 | 内存类型 | 典型应用场景 | |-------|-------|-------|----------------|--------------------------| | 0 | 0 | 0 | **WB** (回写) | 普通内存(默认) | | 0 | 0 | 1 | **WT** (透写) | 显存帧缓冲 | | 0 | 1 | 0 | **UC-** (弱非缓存)| 已弃用 | | 0 | 1 | 1 | **UC** (非缓存) | DMA设备内存 | | 1 | 0 | 0 | **WC** (写合并)| GPU纹理内存 | | 1 | 0 | 1 | **Reserved** | 禁止使用 | | 1 | 1 | 0 | **WP** (写保护)| ROM/敏感数据保护 | | 1 | 1 | 1 | **WT** (透写) | 冗余设计(同010) | > 💡 关键类型:`WC`(Write-Combining)通过合并写入提升显卡/高速设备性能;`UC`(Uncacheable)确保外设直接访问内存[^4]。 #### **3. 地址翻译流程** 当MMU执行页表遍历(Table Walk)时: 1. 定位页表项(PTE) 2. 提取`PAT/PCD/PWT`位组合 3. 查询CPU内部PAT寄存器映射表(MSR 0x277) 4. 确定最终内存类型并传递给缓存子系统 --- ### **三、Linux内核实现** #### **1. 初始化流程** ```c // arch/x86/mm/pat.c void pat_init(void) { rdmsr(MSR_IA32_CR_PAT, pat_msr_val.low, pat_msr_val.high); // 读取CPU PAT配置 __set_pat_entries(); // 设置内核PAT映射表 pat_enabled = true; // 标记PAT已激活 } ``` #### **2. 驱动开发API** ```c // 设置写合并属性(常用于显卡缓冲) pgprot_t prot = pgprot_writecombine(PAGE_KERNEL); void *vaddr = ioremap_cache(phys_addr, size, prot); // 设置非缓存属性(DMA场景) pgprot_t prot = pgprot_noncached(PAGE_KERNEL); void *vaddr = ioremap(phys_addr, size, prot); ``` #### **3. 调试接口** 查看系统PAT配置: ```bash $ cat /sys/kernel/debug/x86/pat_memtype_list RAM/WC: 0x60000000-0x60100000 # 显卡显存区标记为WC RAM/UC: 0x100000000-0x100100000 # PCI设备内存标记为UC ... ``` [^3] --- ### **四、与传统MTRR的对比** | **特性** | PAT | MTRR | |----------------|----------------------|---------------------| | 控制粒度 | 4KB页面 | 物理地址范围(MB级)| | 动态更新 | 修改页表即可 | 需重写MSR寄存器 | | 类型冲突处理 | 按页表优先级 | 固定优先级规则 | | 适用场景 | 精细控制单页缓存策略 | 设置大块内存默认属性| > ⚠️ 实际使用中PAT优先于MTRR:当两者冲突时,PAT设置覆盖MTRR[^4]。 --- ### **五、典型应用场景** 1. **显卡帧缓冲** 使用`WC`类型避免频繁缓存失效,提升渲染性能: ```c // 驱动中映射显存 void *vram = ioremap_wc(phys_addr, size); ``` 2. **DMA数据传输** 设备直接内存访问需`UC`避免缓存一致性问题: ```c dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL); // 隐含UC属性 ``` 3. **内存保护** 敏感数据设为`WP`(Write-Protect)阻止篡改: ```c set_memory_wp((unsigned long)addr, num_pages); ``` --- ### **六、常见问题** 1. **性能下降** 错误配置`UC`类型导致CPU缓存失效 → 检查`/proc/mtrr`和`pat_memtype_list`[^3] 2. **驱动兼容性** 旧驱动忽略PAT位 → 需更新为`pgprot_writecombine()`等API[^4] 3. **虚拟化支持** 虚拟机需透传PAT配置 → 启用EPT/NPT时传递`CR4.PAT`位 --- **相关问题** 1. PAT机制如何处理与MTRR的类型冲突? 2. Linux中如何调试PAT配置错误导致的性能问题? 3. 在KVM虚拟化环境中如何传递PAT设置给Guest OS? 4. 为什么显卡驱动普遍使用`pgprot_writecombine()`?其硬件原理是什么? [^1]: 大页机制对页表遍历路径的优化 [^2]: 页机制的基础原理与实现 [^3]: PAT内存类型调试接口 [^4]: Linux内核PAT实现与驱动API
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值