标签:blog os io for ar 2014 art 问题
题意:
给定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. 强连通缩点+最大权闭合图,布布扣,bubuko.com
HDU 4971 A simple brute force problem. 强连通缩点+最大权闭合图
标签:blog os io for ar 2014 art 问题
原文地址:http://blog.csdn.net/qq574857122/article/details/38734073