题意:
给定n个项目,m个技术难题
下面一行n个数字表示每个项目的收益
下面一行m个数字表示攻克每个技术难题的花费
下面n行第i行表示
第一个数字u表示完成 i 项目需要解决几个技术难题,后面u个数字表示需要解决的问题标号。
下面m*m的矩阵
(i,j) = 1 表示要解决j问题必须先解决i问题。
(若几个问题成环,则需要一起解决)
问:最大收益。
思路:
先给问题缩点一下,每个缩点后的点权就是这个点内所有点权和。
然后跑一个最大权闭合图。
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<vector> #include<set> using namespace std; const int MAXN = 1000;//点数的最大值 const int MAXM = 40010;//边数的最大值 const int INF = 0x3f3f3f3f; #define inf 100000000 struct isap{ struct Edge { int to,next,cap,flow; }edge[MAXM];//注意是MAXM int tol; int Head[MAXN]; int gap[MAXN],dep[MAXN],cur[MAXN]; void add(int u,int v,int w,int rw = 0) { edge[tol].to = v; edge[tol].cap = w; edge[tol].flow = 0; edge[tol].next = Head[u]; Head[u] = tol++; edge[tol].to = u; edge[tol].cap = rw; edge[tol].flow = 0; edge[tol].next = Head[v]; Head[v] = tol++; } int Q[MAXN]; void BFS(int start,int end) { memset(dep,-1,sizeof(dep)); memset(gap,0,sizeof(gap)); gap[0] = 1; int front = 0, rear = 0; dep[end] = 0; Q[rear++] = end; while(front != rear) { int u = Q[front++]; for(int i = Head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if(dep[v] != -1)continue; Q[rear++] = v; dep[v] = dep[u] + 1; gap[dep[v]]++; } } } int S[MAXN]; int sap(int start,int end,int N) { BFS(start,end); memcpy(cur,Head,sizeof(Head)); int top = 0; int u = start; int ans = 0; while(dep[start] < N) { if(u == end) { int Min = INF; int inser; for(int i = 0;i < top;i++) if(Min > edge[S[i]].cap - edge[S[i]].flow) { Min = edge[S[i]].cap - edge[S[i]].flow; inser = i; } for(int i = 0;i < top;i++) { edge[S[i]].flow += Min; edge[S[i]^1].flow -= Min; } ans += Min; top = inser; u = edge[S[top]^1].to; continue; } bool flag = false; int v; for(int i = cur[u]; i != -1; i = edge[i].next) { v = edge[i].to; if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) { flag = true; cur[u] = i; break; } } if(flag) { S[top++] = cur[u]; u = v; continue; } int Min = N; for(int i = Head[u]; i != -1; i = edge[i].next) if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) { Min = dep[edge[i].to]; cur[u] = i; } gap[dep[u]]--; if(!gap[dep[u]])return ans; dep[u] = Min + 1; gap[dep[u]]++; if(u != start)u = edge[S[--top]^1].to; } return ans; } void init(){ tol = 0; memset(Head,-1,sizeof(Head)); } } hehe; inline void rd(int &n){ char c = getchar(); while(c < '0' || c > '9') c = getchar(); n = 0; while(c >= '0' && c <= '9') n *= 10, n += (c - '0'),c = getchar(); } #define N 52 //N为最大点数 #define M 3000 //M为最大边数 struct Edge{ int from, to, nex; bool sign;//是否为桥 }edge[M]; int head[N], edgenum; void add(int u, int v){//边的起点和终点 Edge E={u, v, head[u], false}; edge[edgenum] = E; head[u] = edgenum++; } int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳) int taj;//连通分支标号,从1开始 int Belong[N];//Belong[i] 表示i点属于的连通分支 bool Instack[N]; vector<int> bcc[N]; //标号从1开始 void tarjan(int u ,int fa){ DFN[u] = Low[u] = ++ Time ; Stack[top ++ ] = u ; Instack[u] = 1 ; for (int i = head[u] ; ~i ; i = edge[i].nex ){ int v = edge[i].to ; if(DFN[v] == -1) { tarjan(v , u) ; Low[u] = min(Low[u] ,Low[v]) ; if(DFN[u] < Low[v]) { edge[i].sign = 1;//为割桥 } } else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; } if(Low[u] == DFN[u]){ int now; taj ++ ; bcc[taj].clear(); do{ now = Stack[-- top] ; Instack[now] = 0 ; Belong [now] = taj ; bcc[taj].push_back(now); }while(now != u) ; } } void tarjan_init(int all){ memset(DFN, -1, sizeof(DFN)); memset(Instack, 0, sizeof(Instack)); top = Time = taj = 0; for(int i=0;i<all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!! } vector<int>G[N]; int du[N]; void suodian(){ memset(du, 0, sizeof(du)); for(int i = 1; i <= taj; i++)G[i].clear(); for(int i = 0; i < edgenum; i++){ int u = Belong[edge[i].from], v = Belong[edge[i].to]; if(u!=v)G[u].push_back(v), du[v]++; } } void init(){memset(head, -1, sizeof(head)); edgenum=0;} int ans, n, m; int a[22], b[52], c[52], sum; vector<int>D[22]; set<int>myset[22]; void input(){ int i, j, u; rd(n); rd(m); sum = 0; for(i = 0; i < n; i++)rd(a[i]), sum += a[i]; for(i = 0; i < m; i++)rd(b[i]); for(i = 0; i < n; i++) { myset[i].clear(); D[i].clear(); rd(j); while(j--){ rd(u); D[i].push_back(u); } } init(); for(i = 0; i < m; i++){ for(j = 0; j < m; j++) { rd(u); if(u)add(i, j); } } tarjan_init(m); suodian(); for(i = 1; i <= taj; i++){ c[i] = 0; for(j = 0; j < bcc[i].size(); j++) c[i] += b[bcc[i][j]]; } } int main() { int i, j, T, Cas = 1;rd(T); while(T--){ input(); ans = 0; hehe.init(); int from = n+m, to = n+m+1; for(i = 1; i <= taj; i++) { hehe.add(i+n-1, to, c[i]); for(j = 0; j < G[i].size(); j++) hehe.add(i+n-1, G[i][j]+n-1, inf); } for(i = 0; i < n; i++){ hehe.add(from, i, a[i]); for(j = 0; j < D[i].size(); j++) { if(myset[i].find(Belong[D[i][j]]+n-1)==myset[i].end()) hehe.add(i, Belong[D[i][j]]+n-1, inf); myset[i].insert(Belong[D[i][j]]+n-1); } } ans = hehe.sap(from, to, n+m+2); ans = sum - ans; printf("Case #%d: %d\n", Cas++, ans); } return 0; }
HDU 4971 A simple brute force problem. 强连通缩点+最大权闭合图
时间: 2024-10-12 11:34:43