hdu 4966 (最小树形图)

来源:互联网 发布:u3d编程是什么语言 编辑:程序博客网 时间:2024/06/13 11:55

GGS-DDU

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 72    Accepted Submission(s): 36


Problem Description
Do you think this is a strange problem name? That is because you don't know its full name---'Good Good Study and Day Day Up!". Very famous sentence! Isn't it?

Now "GGS-DDU" is lzqxh's target! He has N courses and every course is divided into a plurality of levels. Just like College English have Level 4 and Level 6.

To simplify the problem, we suppose that the i-th course has Levels from level 0 to level a[i]. And at the beginning, lzqxh is at Level 0 of every course. Because his target is "GGS-DDU", lzqxh wants to reach the highest Level of every course. 

Fortunately, there are M tutorial classes. The i-th tutoial class requires that students must reach at least Level L1[i] of course c[i] before class begins. And after finishing the i-th tutorial class, the students will reach Level L2[i] of course d[i]. The i-th tutoial class costs lzqxh money[i]. 

For example, there is a tutorial class only students who reach at least Level 5 of "Tiyu" can apply. And after finishing this class, the student's "MeiShu" will reach Level 10 if his "MeiShu"'s Level is lower than 10. (Don't ask me why! Supernatural class!!!")

Now you task is to help lzqxh to compute the minimum cost!
 

Input
The input contains multiple test cases.

The first line of each case consists of two integers, N (N<=50) and M (M<=2000). 
The following line contains N integers, representing a[1] to a[N]. The sum of a[1] to a[N] will not exceed 500. 
The next M lines, each have five integers, indicating c[i], L1[i], d[i], L2[i] and money[i] (1<=c[i], d[i]<=N, 0<=L1[i]<=a[c[i]], 0<=L2[i]<=a[d[i]], money[i]<=1000) for the i-th tutorial class. The courses are numbered from 1 to N.

The input is terminated by N = M = 0.
 

Output
Output the minimum cost for achieving lzqxh's target in a line. If his target can't be achieved, just output -1.
 

Sample Input
3 43 3 11 0 2 3 102 1 1 2 101 2 3 1 103 1 1 3 100 0
 

Sample Output
40
 

Source
2014 Multi-University Training Contest 9
 
比赛时一直以为是网络流,然后发现建图搞不出来,就以为是dp,但是点数又太多,没法状态压缩,就不知道怎么做了。赛后知道是最小树形图,恍然大悟。。。怎么可以这么裸。。。今天学弟们都搞了7题,自惭形秽 orz
#include<cstdio>#include<map>#include<queue>#include<cstring>#include<iostream>#include<algorithm>#include<vector>#include<list>#include<set>#include<cmath>using namespace std;const int maxn = 600 + 5;const int INF = 1e9;const double eps = 1e-6;typedef unsigned long long ULL;typedef long long LL;typedef pair<int, int> P;#define fi first#define se second// 固定根的最小树型图,邻接矩阵写法,时间复杂度O(n^3)struct MDST {  int n;  int w[maxn][maxn]; // 边权  int vis[maxn];     // 访问标记,仅用来判断无解  int ans;           // 计算答案  int removed[maxn]; // 每个点是否被删除  int cid[maxn];     // 所在圈编号  int pre[maxn];     // 最小入边的起点  int iw[maxn];      // 最小入边的权值  int max_cid;       // 最大圈编号  void init(int n) {    this->n = n;    for(int i = 0; i < n; i++)      for(int j = 0; j < n; j++) w[i][j] = INF;  }  void AddEdge(int u, int v, int cost) {    w[u][v] = min(w[u][v], cost); // 重边取权最小的  }  // 从s出发能到达多少个结点  int dfs(int s) {    vis[s] = 1;    int ans = 1;    for(int i = 0; i < n; i++)      if(!vis[i] && w[s][i] < INF) ans += dfs(i);    return ans;  }  // 从u出发沿着pre指针找圈  bool cycle(int u) {    max_cid++;    int v = u;    while(cid[v] != max_cid) { cid[v] = max_cid; v = pre[v]; }    return v == u;  }  // 计算u的最小入弧,入弧起点不得在圈c中  void update(int u) {    iw[u] = INF;    for(int i = 0; i < n; i++)      if(!removed[i] && w[i][u] < iw[u]) {        iw[u] = w[i][u];        pre[u] = i;      }  }  // 根结点为s,如果失败则返回false  bool solve(int s) {    memset(vis, 0, sizeof(vis));    if(dfs(s) != n) return false;    memset(removed, 0, sizeof(removed));    memset(cid, 0, sizeof(cid));    for(int u = 0; u < n; u++) update(u);    pre[s] = s; iw[s] = 0; // 根结点特殊处理    ans = max_cid = 0;    for(;;) {      bool have_cycle = false;      for(int u = 0; u < n; u++) if(u != s && !removed[u] && cycle(u)){        have_cycle = true;        // 以下代码缩圈,圈上除了u之外的结点均删除        int v = u;        do {          if(v != u) removed[v] = 1;          ans += iw[v];          // 对于圈外点i,把边i->v改成i->u(并调整权值);v->i改为u->i          // 注意圈上可能还有一个v'使得i->v'或者v'->i存在,因此只保留权值最小的i->u和u->i          for(int i = 0; i < n; i++) if(cid[i] != cid[u] && !removed[i]) {            if(w[i][v] < INF) w[i][u] = min(w[i][u], w[i][v]-iw[v]);            w[u][i] = min(w[u][i], w[v][i]);            if(pre[i] == v) pre[i] = u;          }          v = pre[v];        } while(v != u);        update(u);        break;      }      if(!have_cycle) break;    }    for(int i = 0; i < n; i++)      if(!removed[i]) ans += iw[i];    return true;  }};MDST solver;int a[maxn], id[maxn];int main(){    int n, m;    while(scanf("%d%d", &n, &m)){        if(n == 0 && m == 0)            break;        id[1] = 0;        int total = 0;        for(int i = 1;i <= n;i++){            scanf("%d", &a[i]);            id[i+1] = id[i]+a[i]+1;            total += a[i]+1;        }        solver.init(total+1);        int root = 0;        for(int i = 1;i <= n;i++){            solver.AddEdge(0, id[i]+1, 0);            for(int j = 1;j <= a[i];j++){                solver.AddEdge(id[i]+j+1, id[i]+j, 0);            }        }        while(m--){            int c, l1, d, l2, cost;            scanf("%d%d%d%d%d", &c, &l1, &d, &l2, &cost);            solver.AddEdge(id[c]+l1+1, id[d]+l2+1, cost);        }        if(solver.solve(root))            printf("%d\n", solver.ans);        else            printf("-1\n");    }    return 0;}


0 0
原创粉丝点击