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

说是最大权闭合图....

比赛时没敢写....

题意

一共有n个任务,m个技术

完成一个任务可盈利一些钱,学习一个技术要花费钱

完成某个任务前需要先学习某几个技术

但是可能在学习一个任务前需要学习另几个任务

求最多能赚多少钱咯

先将缩点将需要一起学掉的技术缩成一个点

建s--任务 权值为该任务盈利多少钱

建技术(缩点后)-t 权值为学习这技术的花费(总)

任务-技术 (完成该任务所需的每个技术都需要建边)权值为INF

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
#include<ctype.h>
#include<math.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#include<queue>
#include<stack>
#include<vector>
#include<deque>
#include<set>
#include<map>
//N为最大点数
#define M 150
//M为最大边数
const int MAXN = 2999;//点数的最大值
const int MAXM = 2222;//边数的最大值
const int INF = 0x3f3f3f3f;
struct Edge{
    int from, to, nex;
    bool sign;//是否为桥
}edge[M<<1];
int head[MAXN], edgenum;
void add(int u, int v){//边的起点和终点
    Edge E={u, v, head[u], false};
    edge[edgenum] = E;
    head[u] = edgenum++;
}

int DFN[333], Low[333], Stack[333], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[333];//Belong[i] 表示i点属于的连通分支
bool Instack[333];
vector<int> bcc[333]; //标号从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=1;i<=all;i++)
        if(DFN[i]==-1 )
            tarjan(i, i); //注意开始点标!!!
}
vector<int>G[333];
int du[333];
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]++;
        }
    }
}
struct Edge1
{
    int to,next,cap,flow;
}edge1[MAXM];//注意是MAXM
int tol;
int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
//加边,单向图三个参数,双向图四个参数
void addedge (int u,int v,int w,int rw=0)
{
    edge1[tol].to = v;edge1[tol].cap = w;edge1[tol].next = head[u];
    edge1[tol].flow = 0;head[u] = tol++;
    edge1[tol].to = u;edge1[tol].cap = rw;edge1[tol]. next = head[v];
    edge1[tol].flow = 0;head[v]=tol++;
}
//输入参数:起点、终点、点的总数
//点的编号没有影响,只要输入点的总数
int sap(int start,int end, int N)
{
    memset(gap,0,sizeof(gap));
    memset(dep,0,sizeof(dep));
    memcpy(cur,head,sizeof(head));
    int u = start;
    pre[u] = -1;
    gap[0] = N;
    int ans = 0;
    int i;
    while(dep[start] < N)
    {
        if(u == end)
        {
            int Min = INF;
            for( i = pre[u];i != -1; i = pre[edge1[i^1]. to])
            {
                if(Min > edge1[i].cap - edge1[i]. flow)
                    Min = edge1[i].cap - edge1[i].flow;
            }
            for( i = pre[u];i != -1; i = pre[edge1[i^1]. to])
            {
                edge1[i].flow += Min;
                edge1[i^1].flow -= Min;
            }
            u = start;
            ans += Min;
            continue;
        }
        bool flag =  false;
        int v;
        for( i = cur[u]; i != -1;i = edge1[i].next)
        {
            v = edge1[i]. to;
            if(edge1[i].cap - edge1[i].flow && dep[v]+1 == dep[u])
            {
                flag =  true;
                cur[u] = pre[v] = i;
                break;
            }
        }
        if(flag)
        {
            u = v;
            continue;
        }
        int Min = N;
        for( i = head[u]; i !=  -1;i = edge1[i]. next)
        {
            if(edge1[i].cap - edge1[i].flow && dep[edge1[i].to] < Min)
            {
                Min = dep[edge1[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 = edge1[pre[u]^1].to;
    }
    return ans;
}
int a[MAXN],b[MAXN],cost[MAXN];
vector<int>q[222];
void init(){memset(head, -1, sizeof(head)); edgenum=0;tol=0;}
int main()
{
    int t,cas=1;
    int n, m;//n m 为点数和边数
  //  freopen("in.txt","r",stdin);
    scanf("%d",&t);
    while(t--)
    {
        init();
        int sum=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        for(int i=1;i<=m;i++)
        {
            cost[i]=0;
            scanf("%d",&b[i]);
        }
        for(int i=1;i<=n;i++)
        {
            int k,c;
            q[i].clear();
            scanf("%d",&k);
            for(int j=0;j<k;j++)
            {
                scanf("%d",&c);
                q[i].push_back(c);
            }
        }
        for(int i=1;i<=m;i++)
            for(int j=1;j<=m;j++)
            {
                int a;
                scanf("%d",&a);
                if(a)
                    add(i,j);
            }
        tarjan_init(m);
        suodian();
        for(int i=1;i<=m;i++)
        {
            int v=Belong[i];
            cost[v]+=b[i];
        }
        init();
        int s=0,end=taj+n+1;
        for(int i=1;i<=n;i++)
        {
            addedge(s,taj+i,a[i],0);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<q[i].size();j++)
            {
                addedge(taj+i,Belong[q[i][j]+1],INF,0);
            }
        }
        for(int i=1;i<=taj;i++)
        {
            addedge(i,end,cost[i],0);
            for(int j=0;j<G[i].size();j++)
            {
                addedge(i,G[i][j],INF,0);
            }
        }
        printf("Case #%d: ",cas++);
        printf("%d\n",sum-sap(s,end,taj+n+2));
    }
    return 0;
}

【最小割】HDU 4971 A simple brute force problem.,布布扣,bubuko.com

时间: 2024-12-06 08:20:22

【最小割】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

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. (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.(最小割,最大权闭合图)

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.

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. 其实就是一个闭合权问题模板,官方题解说如果技术之间存在相互的关系需要缩点,其实不用缩点也