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

来源:互联网 发布:slack mac 编辑:程序博客网 时间:2024/06/04 17:46

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

比赛时没敢写....

题意

一共有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];//注意是MAXMint 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;}


0 0