首页 > 代码库 > HDU 4971 A simple brute force problem. 强连通缩点+最大权闭合图

HDU 4971 A simple brute force problem. 强连通缩点+最大权闭合图


题意:

给定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;
}