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

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

时间: 2024-10-12 11:34:43

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

HDU 4971 A simple brute force problem.(最小割,最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=4971 A simple brute force problem. Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 182    Accepted Submission(s): 115 Problem Description There's a com

HDU 4971 A simple brute force problem.(dp)

HDU 4971 A simple brute force problem. 题目链接 官方题解写的正解是最大闭合权,但是比赛的时候用状态压缩的dp也过掉了- -,还跑得挺快 思路:先利用dfs预处理出每个项目要完成的技术集合,那么dp[i][j]表示第i个项目,已经完成了j集合的技术,由于j这维很大,所以利用map去开数组 代码: #include <cstdio> #include <cstring> #include <algorithm> #include &l

【最小割】HDU 4971 A simple brute force problem.

说是最大权闭合图.... 比赛时没敢写.... 题意 一共有n个任务,m个技术 完成一个任务可盈利一些钱,学习一个技术要花费钱 完成某个任务前需要先学习某几个技术 但是可能在学习一个任务前需要学习另几个任务 求最多能赚多少钱咯 先将缩点将需要一起学掉的技术缩成一个点 建s--任务 权值为该任务盈利多少钱 建技术(缩点后)-t 权值为学习这技术的花费(总) 任务-技术 (完成该任务所需的每个技术都需要建边)权值为INF #include<stdio.h> #include<stdlib.h

HDU - 4971 A simple brute force problem. (DP)

Problem Description There's a company with several projects to be done. Finish a project will get you profits. However, there are some technical problems for some specific projects. To solve the problem, the manager will train his employee which may

HDU 4971 A simple brute force problem.

A simple brute force problem. Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on HDU. Original ID: 497164-bit integer IO format: %I64d      Java class name: Main There's a company with several projects to be done. Finish a projec

HDU 4971 A simple brute force problem. 最大权闭合图

点击打开链接 A simple brute force problem. Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 157    Accepted Submission(s): 99 Problem Description There's a company with several projects to be done. Fi

HDU 4971 A simple brute force problem.(最小割---最大权闭合)

题目地址:HDU 4971 比赛的时候还不会最大权闭合,当时还跟队友讨论了好长时间费用流和DP..现在看来就是一最大权闭合水题... 建图思路:源点连工程,权值为利润,汇点连科技项目,权值为花费,然后对有依赖性的连有向边.用正权值和减去最小割就是答案. #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #in

HDU 4971 A simple brute force problem.(最大权闭合图)

题意:n个项目m个问题,完成每个项目有对应收入,解决每个问题需要对应花费, 给出每个项目需解决的问题以及各问题间的依赖关系,求最大利润(可完成部分或全部项目): 思路:网络流中的最大权闭合图的典型应用——利润问题,参考07年day2国家集训队胡伯涛的论文: 参考:http://blog.csdn.net/u012965890/article/details/38761043 闭合图指所有顶点的出边指向的所有顶点均在图中: 最大权闭合图:点权和最大的闭合图,即权值最大的子闭合图: 建图思路: 工程

hdoj 4971 A simple brute force problem. 【最大闭合权 --&gt; 最小割】

题目:hdoj 4971 A simple brute force problem. 题意:给出 n 个任务和 m 项技术,完成某个任务需要其中几项技术,完成某个任务有奖金,学习某个技术需要钱,技术之间有父子关系,某项技术可能需要先学习其他技术,然后问你选择做那些任务获得收益最大? 分析:看题意的黑体字部分,就是一个标准的闭合权问题,这个题目的关键忽悠点在于技术之间的关系,导致很多人想到了dp以及树形dp. 其实就是一个闭合权问题模板,官方题解说如果技术之间存在相互的关系需要缩点,其实不用缩点也