【暑假】[深入动态规划]UVa 1380 A Scheduling Problem

 

 UVa 1380 A Scheduling Problem

 

题目:

 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=41557

思路:

  给出一个任务调度树,单向边u->v表示u必须在v之前完成,双向边u-v表示无所谓方向。

  题目给出定理,首先dfs求得忽略无向边后的最长链点数k,那么问题就是判断是否可以通过无向边定向从而使得最长链点数不超过k。用dp的判断。

  设f[i]表示以i为根的子树中所有的边定向后最长链点数不超过k条件下出边中最长链的最小值,g[i]表示以i为根的子树中所有的边定向后最长链点数不超过k条件下入边中最长链的最小值(最小值是最低限度,如果最小值都不可行那么问题不可行)。

  两种情况(w为子节点):

  1.   与w的边中没有双向边:得出w.f_max与w.g_max判断与k的大小关系 如果超过k 回值INF否则回值w.f_max与w.g_max(max代表最长链)。没有选择的情况,已经定形
  2.   与w的边中有双向边:定向双向边使满足k的限定下f与g尽量小。批量定向,求解f[u]的时候,将w按照f从小到大排序,依此枚举p,对于p将p之前的定向为出边,计算f[u]。同理求解g[u]。最后判断与k的关系。根据双向边定向选择最优结果

 

代码:

  1 // UVa1380 A Scheduling Problem
  2 // Rujia Liu
  3 #include<iostream>
  4 #include<string>
  5 #include<cstring>
  6 #include<sstream>
  7 #include<vector>
  8 #include<algorithm>
  9 using namespace std;
 10 
 11 const int maxn = 200 + 5;
 12 const int INF = 1000000000;
 13 
 14 struct Edge {
 15   int u, v, d; // d=1 means u->v, d=2 means v->u, d=0 means u-v
 16   Edge(int u=0, int v=0, int d=0):u(u),v(v),d(d){}
 17 };
 18 
 19 vector<Edge> edges[maxn];
 20 int n, root, maxlen, f[maxn], g[maxn], have_father[maxn];
 21 
 22 // maximal length of a DIRECTED path starting from u
 23 int dfs(int u) {
 24   int ans = 0;
 25   for(int i = 0; i < edges[u].size(); i++) {
 26     int v = edges[u][i].v;
 27     if(edges[u][i].d == 1)   //u->v 
 28       ans = max(ans, dfs(v)+1);
 29   }
 30   return ans;
 31 }
 32 
 33 bool read_data() {
 34   bool have_data = false;
 35   int a, b;
 36   n = 0;
 37   for(int i = 0; i < maxn; i++) edges[i].clear();
 38   memset(have_father, 0, sizeof(have_father));
 39 
 40   while(cin >> a && a){
 41     string str;
 42     have_data = true;
 43     if(a > n) n = a;
 44     while(cin >> str && str != "0"){
 45       int len = str.length();
 46       char dir = str[len-1];
 47       if(dir == 'd' || dir == 'u') str = str.substr(0, len-1);
 48       stringstream ss(str);
 49       ss >> b; // b is a's son
 50       if(b > n) n = b;
 51       have_father[b] = 1;
 52       if(dir == 'd'){
 53         edges[a].push_back(Edge(a, b, 1)); // forward
 54         edges[b].push_back(Edge(b, a, 2)); // backward
 55       }else if(dir == 'u'){
 56         edges[a].push_back(Edge(a, b, 2));
 57         edges[b].push_back(Edge(b, a, 1));
 58       }else{
 59         edges[a].push_back(Edge(a, b, 0)); // it's a rooted tree, so we don't store edge to father
 60       }
 61     }
 62   }
 63   if(have_data) {
 64     for(int i = 1; i <= n; i++)
 65       if(!have_father[i] && !edges[i].empty()) { root = i; break; }
 66   }
 67   return have_data;
 68 }
 69 
 70 struct UndirectedSon {
 71   int w, f, g;
 72   UndirectedSon(int w=0, int f=0, int g=0):w(w),f(f),g(g){}
 73 };
 74 
 75 bool cmp_f(const UndirectedSon& w1, const UndirectedSon& w2) {
 76   return w1.f < w2.f;
 77 }
 78 
 79 bool cmp_g(const UndirectedSon& w1, const UndirectedSon& w2) {
 80   return w1.g < w2.g;
 81 }
 82 
 83 // calculate f[i] and g[i]
 84 // return true iff f[i] < INF
 85 // f[i] is the minimal length of the longest "->u" path if all subtree paths have length <= maxlen
 86 // g[i] is the minimal length of the longest "u->" path if all subtree paths have length <= maxlen
 87 // f[i] = g[i] = INF if "all subtree paths have length <= maxlen" cannot be satisfied
 88 bool dp(int i, int fa) {
 89   if(edges[i].empty()) {
 90     f[i] = g[i] = 0;
 91     return true;
 92   }
 93   vector<UndirectedSon> sons;
 94   int f0 = 0, g0 = 0; // f'[i] and g'[i] for directed sons
 95 
 96   // let f'[i] = max{f[w] | w->i}+1, g'[i] = max{g[w] | i->w}+1
 97   // then we should change some undirected edges to ->u or u-> edges so that f'[i]+g'[i] <= maxlen
 98   // then f[i] is the minimal f'[i] under this condition, and g[i] is the minimal g'[i]
 99   for(int k = 0; k < edges[i].size(); k++) {
100     int w = edges[i][k].v;
101     if(w == fa) continue;     //ch != fa 
102     dp(w, i);           //Çó½âÍê×Ó½ÚµãºóÇó½âµ±Ç°½áµã 
103     int d = edges[i][k].d;
104     if(d == 0) sons.push_back(UndirectedSon(w, f[w], g[w]));
105     else if(d == 1) g0 = max(g0, g[w]+1);
106     else f0 = max(f0, f[w]+1);
107   }
108   // If there is no undirected edges, we're done
109   if(sons.empty()) {
110     f[i] = f0; g[i] = g0;
111     if(f[i] + g[i] > maxlen) { f[i] = g[i] = INF; }
112     return f[i] < INF;
113   }
114 
115   f[i] = g[i] = INF;
116 
117   // to calculate f[i], we sort f[w] of undirected sons in increasing order and make first p edges to w->i
118   // then we calculate f'[i] and g'[i], check for f'[i]+g'[i] <= maxlen and update answer
119   int s = sons.size();
120   sort(sons.begin(), sons.end(), cmp_f);
121   int maxg[maxn]; // maxg[i] is max{sons[i].g, sons[i+1].g, ...}
122   maxg[s-1] = sons[s-1].g;
123   for(int k = s-2; k >= 0; k--)
124     maxg[k] = max(sons[k].g, maxg[k+1]);
125   for(int p = 0; p <= sons.size(); p++) {
126     int ff = f0, gg = g0;
127     if(p > 0) ff = max(ff, sons[p-1].f+1);
128     if(p < sons.size()) gg = max(gg, maxg[p]+1);
129     if(ff + gg <= maxlen) f[i] = min(f[i], ff);
130   }
131 
132   // g[i] is similar
133   sort(sons.begin(), sons.end(), cmp_g);
134   int maxf[maxn]; // maxf[i] is max{sons[i].f, sons[i+1].f, ...}
135   maxf[s-1] = sons[s-1].f;
136   for(int k = s-2; k >= 0; k--)
137     maxf[k] = max(sons[k].f, maxf[k+1]);
138   for(int p = 0; p <= sons.size(); p++) {
139     int ff = f0, gg = g0;
140     if(p > 0) gg = max(gg, sons[p-1].g+1);
141     if(p < sons.size()) ff = max(ff, maxf[p]+1);
142     if(ff + gg <= maxlen) g[i] = min(g[i], gg);
143   }
144 
145   return f[i] < INF;
146 }
147 
148 int main() {
149   while(read_data()) {    
150     maxlen = 0;
151     for(int i = 1; i <= n; i++) maxlen = max(maxlen, dfs(i));
152     // Note: the problem asks for the number of nodes in path, but all the "lengths" above mean "number of edges"
153     if(dp(root, -1)) cout << maxlen+1 << "\n"; 
154     else cout << maxlen+2 << "\n";
155   }
156   return 0;
157 }
Code from Rujia

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值