1439 - Exclusive Access 2

Having studied mutual exclusion protocols in the previous year’s competition you are now facing a more challenging problem. You have a big enterprise system with a number concurrently running processes. The system has several resources – databases, message queues, etc. Each concurrent process works with two resources at a time. For example, one process might copy a job from a particular database into the message queue, the other process might take a job from the message queue, perform the job, and then put the result into some other message queue, etc.

All resources are protected from concurrent access by mutual exclusion protocols also known as locks. For example, to access a particular database process acquires the lock for this database, then performs its work, then releases the lock. No two processes can hold the same lock at the same time (that is the property of mutual exclusion). Thus, the process that tries to acquire a lock waits if that lock is taken by some other process.

The main loop of the process that works with resources P and Q looks like this:

 loop forever
   DoSomeNonCriticalWork()
   P.lock()
   Q.lock()
   WorkWithResourcesPandQ()
   Q.unlock()
   P.unlock()
 end loop

The order in which locks for resources P and Q are taken is important. Consider a case where process c had acquired lock P with P.lock() and is waiting for lock Q in Q.lock(). It means that lock Q is taken by some other process d. If the process d is working (not waiting), then we say that there is a wait chain of length 1. If d had acquired lock Q and is waiting for another lock R, which is acquired by a working process e, then we say that there is a wait chain of length 2, etc. If any process in this wait chain waits for lock P that is already taken by process c, then we say that the wait chain has infinite length and the system deadlocks.

For this problem, we are interested only in alternating wait chains where processes hold their first locks and wait for the second ones. Formally:

Alternating wait chain of length n (n 0) is an alternating sequence of resources Ri ( 0in + 1) and distinct processes ci ( 0in): R0 c0 R1 c1…Rn cn Rn+1, where process ci acquires locks for resources Ri and Ri+1 in this order. Alternating wait chain is a deadlock when R0 = Rn+1.
You are given a set of resources each process works with. Your task is to decide the order in which each process has to acquire its resource locks, so that the system never deadlocks and the maximum length of any possible alternating wait chain is minimized.

Input

The first line of the input file contains a single integer n ( 1n100) – the number of processes.
The following n lines describe resources that each process needs. Each resource is designated with an uppercase English letter from L to Z, so there are at most 15 resources. Each line describing process contains two different resources separated by a space.

Output

On first line of the output file write a singe integer number m – the minimally possible length of the maximal alternating wait chain.
Then write n lines – one line per process. On each line write two resources in the order they should be taken by the corresponding process to ensure this minimal length of the maximal alternating wait chain. Separate resources on a line by a space. If there are multiple satisfying orderings, then write any of them. The order of the processes in the output should correspond to their order in the input.

Sample Input

2
P Q
R S
6
P Q
Q R
R S
S T
T U
U P
4
P Q
P Q
P Q
P Q
3
P Q
Q R
R P
6
P Q
Q S
S R
R P
P S
R Q
Sample Output

0
P Q
R S
0
P Q
R Q
R S
T S
T U
P U
0
P Q
P Q
P Q
P Q
1
P Q
Q R
P R
2
P Q
Q S
R S
P R
P S
R Q

我有话说:
题目大意: 一个庞大的系统中运行着n个守护进程。每个进程恰好用到两个资源。这些资源不支持并发访问,所以这些进程通过锁来保证互斥访问。
每个进程的主循环如下:
loop forever
DoSomeNonCriticalWork()
P.lock()
Q.lock()
WorkWithResourcesPandQ()
Q.unlock()
P.unlock()
end loop
注意 ,P和Q的顺序至关重要。先获取P先获取Q可能产生截然不同得到效果。给定每个进程所需的两种资源,你的任务是确定每个进程获取所得顺序。是的进程永远不会死锁,且最坏情况下等待的最长链长度最小。
分析:在本题中,一个长度为n的等待链是一个不同资源和不不同进程的交替序列:R0c0R1c1……RncnRn+1.这个定义图中点和边的定义类似。当R0=Rn+1时死锁。所以我们的任务就是会给每一条边确定方向,使图中没有圈,且图中最长路最短。
实现:
我们先把所有的点分成一个个集合。集合中的点没有边。同样我们可以给集合编号,分为p层,标号为为0,1,2……层。定向成从层编号小的的点指向编号大的点。如u在5层v在2层则为v->u.最长路所包含的节点不超过p个。所以这条最长路的节点数不超过p(p越小越好)。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=10+5;
const int maxm=100+5;
int n,m,u[maxm],v[maxm],G[maxn][maxn];
int ind[1<<maxn],d[1<<maxn],best[1<<maxn],label[maxm];

bool independent(int mask)//选择mask中一个相互独立子集
{
    for(int i=0;i<maxn;i++)if(mask&(1<<i))
        for(int j=0;j<maxn;j++)if(mask&(1<<j))
            if(i!=j&&G[i][j])return false;
    return true;
}
int dp(int mask)
{
    int& ans=d[mask];
    if(ans>=0)return ans;
    if(mask==0)return 0;
    ans=maxn+1;
    for(int s=mask;s;s=(s-1)&mask)
    {
        if(ind[s]){
            int t=dp(mask^s)+1;
            if(t<ans){
                ans=t;
                best[mask]=s;
            }
        }
    }
    return ans;
}
void mark(int mask,int c)
{
    for(int i=0;i<maxn;i++)
        if(mask&(1<<i))label[i]=c;
}
int main()
{
    while(scanf("%d",&m)==1&&m)
    {
        memset(G,0,sizeof(G));
        int useful=0;
        char a[9],b[9];
        for(int i=0;i<m;i++)
        {
            scanf("%s%s",a,b);
            u[i]=a[0]-'L';v[i]=b[0]-'L';
            G[u[i]][v[i]]=1;
            useful|=(1<<u[i]);
            useful|=(1<<v[i]);
        }
        memset(ind,0,sizeof(ind));
        for(int mask=useful;mask;mask=(mask-1)&useful)
            if(independent(mask))ind[mask]=1;
        memset(d,-1,sizeof(d));
        int ans=dp(useful);
        printf("%d\n",ans-2);
        int s=useful,k=0;
        while(s)
       {
          mark(s,k++);
          s^=best[s];
       }
       for(int i=0;i<m;i++)
       {
            if(label[u[i]]<label[v[i]])swap(u[i],v[i]);//越小越在下这和上述定义有出入,我们的dp过程是逆推的。
            printf("%c %c\n",'L'+u[i],'L'+v[i]);
       }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值