poj3396 A Scheduling Problem

Description

There is a set of jobs, say x1, x2, …, xn, to be scheduled. Each job
needs one day to complete. Your task is to schedule the jobs so that
they can be finished in a minimum number of days. There are two types
of constraints: Conflict constraints and Precedence constraints.

Conflict constraints: Some pairs of jobs cannot be done on the same
day. (Maybe job xi and job xj need to use the same machine. So they
must be done in different dates).

Precedence constraints: For some pairs of jobs, one needs to be
completed before the other can start. For example, maybe job xi cannot
be started before job xj is completed.

The scheduling needs to satisfy all the constraints.

To record the constraints, we build a graph G whose vertices are the
jobs: x1, x2, …, xn. Connect xi and xj by an undirected edge if xi and
xj cannot be done on the same day. Connect xi and xj by a directed
edge from xi to xj if xi needs to be completed before xj starts.

If the graph is complicated, the scheduling problem is very hard. Now
we assume that for our problems, the constraints are not very
complicated: The graph G we need to consider are always trees (after
omitting the directions of the edges). Your task is to find out the
number of days needed in an optimal scheduling for such inputs. You
can use the following result:

If G is a tree, then the number of days needed is either k or k + 1,
where k is the maximum number of vertices contained in a directed path
of G, i.e., a path P = (x1, x2, …, xk), where for each i = 1, 2, …, k
− 1, there is a directed edge from xi to xi + 1.

Figure 1 below is such an example. There are six jobs: 1, 2, 3, 4, 5,
6. From this figure, we know that job 1 and job 2 must be done in different dates. Job 1 needs to be done before job 3, job 3 before job
5, job 2 before job 4 and job 4 before job 6. It is easy to verify
that the minimum days to finish all the jobs is 4 days. In this
example, the maximum number k of vertices contained in a directed path
is 3.

Figure 1: Example

Input

The input consists of a number of trees (whose edges may be directed
or undirected), say T1, T2, …, Tm, where m ≤ 20. Each tree has at most
200 vertices. We represent each tree as a rooted tree (just for
convenience of presentation, the root is an arbitrarily chosen
vertex). Information of each of the trees are contained in a number of
lines. Each line starts with a vertex (which is a positive integer)
followed by all its sons (which are also positive integers), then
followed by a 0. Note that 0 is not a vertex and it indicates the end
of that line. Now some of the edges are directed. The direction of an
edge can be from father to son, and can also be from son to father. If
the edge is from father to son, then we put a letter “d” after that
son (meaning that it is a downward edge). If the edge is from son to
father, then we put a letter “u” after that son (meaning that it is an
upward edge). If the edge is undirected then we do not put any letter
after the son.

The first case of the sample input below is the example in Figure 1.
这里写图片描述
Consecutive vertices (numbers or numbers with a letter after it) in a
line are separated by a single space. A line containing a single 0
means the end of that tree. The next tree starts in the next line. Two
consecutive lines of single 0 means the end of the input.

Output

The output contains one line for each test case. Each line contains a
number, which is the minimum number of days to finish all the jobs in
that test case.

首先dfs一遍求出最长有向链的长度k,然后只要判断是否能安排无向边使得最长链不超过【实际上就是等于】k。
记f[u]表示在所有合法【最长链<=k】的情况下,从u的子树走到u的最长链的最小值,g[u]为从u走到u的子树。
如果u和子节点的连边都是有向边,只要分别取儿子f和g的最大值加起来,如果合法就取,不合法就设为无穷大。
如果有无向边的话,就需要给无向边定向。下面以求f为例。把所有儿子按照f升序排序,很明显如果取了x,那么所有在x左边的就都应该取【取了不会让f变差,但可能让g变优】。所以只需要扫一遍就能求出解。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define M(a,b) make_pair(a,b)
#define F first
#define S second
const int oo=0x3f3f3f3f;
vector<pair<int,int> > e[210],opt;
int n,k,root,fa[210],dfs1[210],f[210],g[210];
bool cmp_f(pair<int,int> p1,pair<int,int> p2)
{
    return p1.F<p2.F;
}
bool cmp_s(pair<int,int> p1,pair<int,int> p2)
{
    return p1.S<p2.S;
}
bool init()
{
    int u,v,i;
    bool flag=0;
    char c;
    n=0;
    memset(fa,0,sizeof(fa));
    for (i=0;i<=205;i++)
      e[i].clear();
    while (scanf("%d",&u)&&u)
    {
        n=max(n,u);
        flag=1;
        while (scanf("%d",&v)&&v)
        {
            n=max(n,v);
            fa[v]=u;
            c=getchar();
            switch (c)
            {
                case ' ':
                    e[u].push_back(M(v,5));
                    e[v].push_back(M(u,6));
                    break;
                case 'd':
                    e[u].push_back(M(v,2));
                    e[v].push_back(M(u,4));
                    break;
                case 'u':
                    e[u].push_back(M(v,1));
                    e[v].push_back(M(u,3));
                    break;
            }
        }
    }
    for (i=1;i<=n;i++)
      if (!fa[i]) root=i;
    return flag;
}
int dfs(int u)
{
    if (dfs1[u]) return dfs1[u];
    dfs1[u]=1;
    int i,x;
    for (i=0;i<e[u].size();i++)
      if ((x=e[u][i].S)==2||x==3)
        dfs1[u]=max(dfs1[u],dfs(e[u][i].F)+1);
    return dfs1[u];
}
void make()
{
    memset(dfs1,0,sizeof(dfs1));
    k=0;
    int i;
    for (i=1;i<=n;i++)
      k=max(k,dfs(i));
}
void dfs2(int u)
{
    vector<pair<int,int> > opt;
    int f1=0,g1=0,i,j,v,ff,gg,x;
    f[u]=g[u]=oo;
    for (i=0;i<e[u].size();i++)
    {
        if (e[u][i].S==2)
        {
            dfs2(v=e[u][i].F);
            f1=max(f1,f[v]);
        }
        if (e[u][i].S==1)
        {
            dfs2(v=e[u][i].F);
            g1=max(g1,g[v]);
        }
        if (e[u][i].S==5)
        {
            dfs2(v=e[u][i].F);
            opt.push_back(M(f[v],g[v]));
        }
    }
    if (opt.empty())
    {
        if (f1+g1+1<=k)
        {
            f[u]=f1+1;
            g[u]=g1+1;
        }
        return;
    }
    sort(opt.begin(),opt.end(),cmp_f);
    gg=0;
    for (i=opt.size()-1;i>=-1;i--)
    {
        if (i!=(opt.size()-1))
          gg=max(gg,opt[i+1].S);
        if (i>=0) x=max(f1,opt[i].F);
        else x=f1;
        if (x+max(gg,g1)+1<=k)
          f[u]=min(f[u],x+1);
    }
    sort(opt.begin(),opt.end(),cmp_s);
    ff=0;
    for (i=opt.size()-1;i>=-1;i--)
    {
        if (i!=opt.size()-1) ff=max(ff,opt[i+1].F);
        if (i>=0) x=max(g1,opt[i].S);
        else x=g1;
        if (x+max(ff,f1)+1<=k)
          g[u]=min(g[u],x+1);
    }
}
bool ok()
{
    dfs2(root);
    return f[root]<oo;
}
int main()
{
    while (init())
    {
        make();
        if (ok()) printf("%d\n",k);
        else printf("%d\n",k+1);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值