【動態規劃】關鍵子工程

在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为1、
2、……、N;这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某些
子工程完成之后才能施工。由于子工程之间有相互依赖关系,因此有两个任务需要我们去完
成:首先,我们需要计算整个工程最少的完成时间;同时,由于一些不可预测的客观因素会
使某些子工程延期,因此我们必须知道哪些子工程的延期会影响整个工程的延期,我们把有
这种特征的子工程称为关键子工程,因此第二个任务就是找出所有的关键子工程,以便集中
精力管理好这些子工程,尽量避免这些子工程延期,达到用最快的速度完成整个工程。为了
便于编程,现在我们假设: 
(1)根据预算,每一个子工程都有一个完成时间。 
(2)子工程之间的依赖关系是:部分子工程必须在一些子工程完成之后才开工。 
(3)只要满足子工程间的依赖关系,在任何时刻可以有任何多个子工程同时在施工,
也既同时施工的子工程个数不受限制。 
(4)整个工程的完成是指:所有子工程的完成。 
例如,有五个子工程的工程规划表: 
序号    完成时间  子工程1  子工程2  子工程3  子工程4  子工程5 
子工程1       5       -       0       0       0       0
子工程2       4       0       -       0       0       0
子工程3      12       0       0       -       0       0
子工程4       7       1       1       0       -       0
子工程5       2       1       1       1       1       -
其中,表格中第 I+1 行 J+2 列的值如为 0 表示“子工程 I”可以在“子工程 J”没完成
前施工,为 1 表示“子工程 I”必须在“子工程 J”完成后才能施工。上述工程最快完成时
间为 14 天,其中子工程1、3、4、5为关键子工程。 
 
又例如,有五个子工程的工程规划表: 
序号  完成时间  子工程1  子工程2  子工程3  子工程4  子工程5 
子工程1     5       -       0       1       0       0
子工程2     4       0       -       0       0       0
子工程3    12       0       0       -       1       0
子工程4     7       1       1       0       -       0 
子工程5     2       1       1       1       1       -
上述的子工程划分不合理,因为无法安排子工程1,3,4的施工。 
 
输入数据: 
第 1 行为N,N是子工程的总个数,N≤200。 
第2 行为N个正整数,分别代表子工程1、2、……、N的完成时间。 
第 3 行到 N+2 行,每行有 N-1 个 0 或 1。其中的第 I+2 行的这些 0,1,分别表示“子
工程 I”与子工程1、2、…、I-1、I+1、…N的依赖关系,(I=1、2、……、N)。每行数据之
间均用一个空格分开。 
 
输出数据:  
如子工程划分不合理,则输出-1; 
如子工程划分合理,则用两行输出:第1行为整个工程最少的完成时间。第2行为按由
小到大顺序输出所有关键子工程的编号。 
 
样例: 
输入文件名:project.in 
5                               
5 4 12 7 2                   
0 0 0 0 
0 0 0 0 
0 0 0 0 
1 1 0 0 
1 1 1 1 
 
输出文件名:project.out 
14 
1 3 4 5 
可用記憶化搜索或者拓撲排序解決。
狀態:用f[i]表示完成i所需的最小時間及等待時間。
轉移方程:f[i] = max(f[j]) + t[i],其中,j是i的前驅結點。
ACCode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
using std::bitset;
using std::max;

const char fi[] = "project.in";
const char fo[] = "project.out";
const int maxN = 210;

struct Edge{int dest; Edge *next; };
Edge *edge[maxN];
int f[maxN];
int t[maxN];
int Ou[maxN];
bitset <maxN> key;
bitset <maxN> marked;
int n, ok;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
  
  void insert(int u, int v)
  {
    Edge *p = new Edge;
    p -> dest = v;
    p -> next = edge[u];
    edge[u] = p;
  }
  
  void readdata()
  {
    scanf("%d", &n);
    for (int i = 1; i < n + 1; ++i)
      scanf("%d", t + i);
    for (int i = 1; i < n + 1; ++i)
     for (int j = 1; j < n + 1; ++j)
      if (i != j)
      {
        scanf("%d", &ok);
        if (ok) {insert(i, j); ++Ou[j]; }
      }
    for (int i = 1; i < n + 1; ++i)
     if (Ou[i] == 0) insert(0, i);
  }
  
  void DP(int u)
  {
    if (f[u]) return; //要記憶化。
    marked.set(u);
    for (Edge *p = edge[u]; p; p = p -> next)
    {
      int v = p -> dest;
      if (marked.test(v))
        {printf("-1"); exit(0); }
	//判斷是否存在環。
      DP(v);
      f[u] = max(f[u], f[v]);
    }
    f[u] += t[u];
    marked.reset(u); //要記得清空標記。
  }

  void Find(int u)
  {
    int Max = 0;
    for (Edge *p = edge[u]; p; p = p -> next)
      Max = max(Max, f[p -> dest]);
    for (Edge *p = edge[u]; p; p = p -> next)
     if (f[p -> dest] == Max)
       {key.set(p -> dest); Find(p -> dest); }
  }
  
  void work()
  {
    DP(0);
    Find(0);
    printf("%d\n", f[0]);
    for (int i = 1; i < n + 1; ++i)
     if (key.test(i)) printf("%d ", i);
  }
  
int main()
{
  init_file();
  readdata();
  work();
  exit(0);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值