FZU - 2295 Human life (最大权闭合子图)
题目链接
题目分析
题意:你在玩一个游戏,在其中你可以通过学习一些技能,但是学习某些技能之前,可能还要学习一些其他的技能,并且学习任何技能都有一定的花费;
而我们可以通过掌握某些工作以获取报酬,为了掌握这一工作,我们必须学会特定的技能。
不过有些工作彼此之间是冲突的,简单来说:如果你掌握了工作A,那么将无法掌握工作B
思路:
由于技能之间也存在依赖关系,但实际上如果要获取某一工作的报酬,那么必须选择这个工作的前置技能以及前置技能的前置技能,
那么显然,这些形成依赖关系的技能都是这一工作的前置技能,所以我们的问题就是求最大权闭合子图了。
我们在工作和其所有的前置技能之间建一条容量为inf的边,然后由所有的技能向汇点建一条容量为这一技能学习消耗的边,
再由源点向每个工作建一条容量为这一工作报酬的边。
这个地方还加上了一个条件,有些工作无法同时获取,不过这个地方产生冲突最大对数为5个,那么我们枚举所有的情况就好了,
一共2^5 = 32种,根据每种状态来决定可选取的工作,并构建这一顶点及其相关的边
然后根据:最大闭合子图的权值 = 所有权值为正的结点的权值之和 - 最小割(最大流)求出答案
顺便吐糟一下这个题,首先这个OJ的C++环境不是C11标准,也就是说不支持大括号给结构体变量赋值,比如这样:
我这个语法写了近一年了,从未出错,这个评测机第一次把我这里卡了,一直CE,还不给出错误信息QAQ....
代码区
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + ;
const int Max = 1e3+ ; struct Edge
{
int to, next, flow;
}edge[Max << ]; int T, n, m, k;
int s, t;
vector<int>edge_raw[Max]; //记录原图边的关系
int vis[][]; //vis[i][j]记录j是否是i的前置结点
int select[]; //记录某点是否被删除(处理工作冲突)
int use[]; //记录每个点的是否使用(处理技能之间的前置关系)
int head[], tot;
int cost[]; //记录了学习每个技能的花费
int val[]; //记录掌握每个工作的报酬
int dis[]; //记录i的层次编号(Dinic中使用)
pair<int, int>fight[]; //记录冲突 void init()
{
s = , t = n + m + ; //1-m为技能,m+1~n+m为工作
memset(vis, , sizeof(vis));
memset(use, , sizeof(use));
for (int i = ;i <= n; i++)
edge_raw[i].clear();
} void add(int u, int v, int flow)
{
edge[tot].to = v;
edge[tot].flow = flow;
edge[tot].next = head[u];
head[u] = tot++; edge[tot].to = u;
edge[tot].flow = ;
edge[tot].next = head[v];
head[v] = tot++;
} void dfs(int u) //找到每个技能所有的前置技能
{
use[u] = true; //代表u已经访问
for (int i = ; i < edge_raw[u].size(); i++)
{
int v = edge_raw[u][i];
vis[u][v] = true;
if (!use[v]) //自己是自己的前置结点
{
dfs(v);
}
for (int j = ;j <= n;j++) //枚举这个点的前置结点
{
if (vis[v][j]) //v的前置结点是j,那么u的前置结点也是j
{
vis[u][j] = true;
}
}
}
} bool bfs() //判断连通性,将图分层次
{
queue<int>q;
memset(dis, -, sizeof(dis));
dis[s] = ;
q.push(s);
while (!q.empty())
{
int u = q.front();q.pop(); for (int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].to;
if (dis[v] == - && edge[i].flow > )
{
dis[v] = dis[u] + ;
q.push(v);
if (v == t) return true;
}
}
}
return false;
} int dfs(int u, int flow_in)
{
if (u == t) return flow_in;
int flow_out = ; //实际流出流量
for (int i = head[u];i != -;i = edge[i].next)
{
int v = edge[i].to;
if (dis[v] == dis[u] + && edge[i].flow > )
{
int flow_part = dfs(v, min(flow_in, edge[i].flow));
if (flow_part == )continue; //无法形成增广路
flow_in -= flow_part;
flow_out += flow_part;
edge[i].flow -= flow_part;
edge[i ^ ].flow += flow_part;
if (flow_in == )break;
}
}
return flow_out;
} int max_flow()
{
int sum = ;
while (bfs())
{
sum += dfs(s, inf);
}
return sum;
} int main()
{
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d", &T);
while (T--)
{
scanf("%d%d%d", &n, &m, &k);
init();
for (int i = , num;i <= n;i++)
{
scanf("%d%d", cost + i, &num);
for (int j = ;j <= num;j++)
{
int pre; //技能i的前置技能点
scanf("%d", &pre);
edge_raw[i].push_back(pre); //构建直系前置技能关系
}
}
for (int i = ;i <= n;i++)
{
if (!use[i])dfs(i);
} for (int i = n + , cnt; i <= n + m;i++) //工作编号n +1 ~n+m
{
scanf("%d%d", val + i, &cnt);
while (cnt--)
{
int x;
scanf("%d", &x);
vis[i][x] = true;
for (int j = ;j <= n;j++)
{
if (vis[x][j]) //求出工作所有的前置技能
vis[i][j] = true;
}
}
} for (int i = ;i < k;i++)
{
scanf("%d %d", &fight[i].first, &fight[i].second);
}
int max_val = ; for (int state = ;state < ( << k);state++) //枚举状态,对应位置为1表示选first,0表示选second
{
memset(select, , sizeof(select)); //0表示不删除
memset(head, -, sizeof(head));tot = ;
for (int i = ;i < k;i++)
{
if ((state >> i) & )
{
select[fight[i].second] = true; //删除second
}
else
{
select[fight[i].first] = true; //删除first
}
}
int sum = ; //记录总价值
for (int i = + n;i <= n + m;i++)
{
if (select[i - n])continue; //当前状态下不不选取的工作就不用构建与之有关的边了
sum += val[i];
add(s, i, val[i]); //由源点向可选工作构建一条容量为当前工作报酬的边
for (int j = ;j <= n;j++)
{
if (vis[i][j])
{
add(i, j, inf); //有工作向其所有前置技能点建一条容量为inf的边
}
}
}
for (int i = ;i <= n;i++) //由所有技能向汇点构建一条容量为其花费的边
{
add(i, t, cost[i]);
}
int flow = max_flow();
max_val = max(max_val, sum - flow); //最大闭合子图的权值 = 所有权值为正的结点的权值之和 - 最小割(最大流)
}
printf("%d\n", max_val);
}
return ;
}
FZU 2295
FZU - 2295 Human life (最大权闭合子图)的更多相关文章
- FZU - 2295 Human life:网络流-最大权闭合子图-二进制优化-第九届福建省大学生程序设计竞赛
目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 http://acm.fzu.edu.cn/problem.php?pid=2295 htt ...
- Human life FZU - 2295 最大权闭合子图(第一次遇到被教育了)
Xzz is playing a MMORPG "human life". In this game, there are N different skills. Some ski ...
- BZOJ1565 [NOI2009]植物大战僵尸(拓扑排序 + 最大权闭合子图)
题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=1565 Description Input Output 仅包含一个整数,表示可以 ...
- HDU 3879 Base Station(最大权闭合子图)
经典例题,好像说可以转化成maxflow(n,n+m),暂时只可以勉强理解maxflow(n+m,n+m)的做法. 题意:输入n个点,m条边的无向图.点权为负,边权为正,点权为代价,边权为获益,输出最 ...
- [BZOJ 1497][NOI 2006]最大获利(最大权闭合子图)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1497 分析: 这是在有向图中的问题,且边依赖于点,有向图中存在点.边之间的依赖关系可以 ...
- HDU4971 A simple brute force problem.(强连通分量缩点 + 最大权闭合子图)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4971 Description There's a company with several ...
- HDU5855 Less Time, More profit(最大权闭合子图)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5855 Description The city planners plan to build ...
- HDU5772 String problem(最大权闭合子图)
题目..说了很多东西 官方题解是这么说的: 首先将点分为3类 第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分) 第二类:原串中的n个点每个 ...
- SCU3109 Space flight(最大权闭合子图)
嗯,裸的最大权闭合子图. #include<cstdio> #include<cstring> #include<queue> #include<algori ...
随机推荐
- 使用Python调用Zabbix API
Zabbix API官方文档: https://www.zabbix.com/documentation/4.0/zh/manual/api 1.向 api_jsonrpc.php 发送HTTP_PO ...
- 通过pro文件使Qt的build目录更清爽
1.指定moc存放的路径,Qt moc编译器生成的moc文件 unix:MOC_DIR = ../tmp win32:MOC_DIR = ../tmp 2.指定目标文件存放的路径,生成的dll或者ex ...
- 1.linux 基本操作和命令
整理复习之前的linux学习笔记,正好贴出来了. 1.[root@chen ~]# [当前登录用户@主机名 当前所在目录]# 当前用户身份 #号表示管理员root $号表示 ...
- C++ 中virtual 用法
一.virtual 修饰基类中的函数,派生类重写该函数: #include using namespace std; class A{ public: virtual void display(){ ...
- ES6展开运算符数组合并,函数传参
定义: .展开运算符允许一个表达式在某处展开. 使用场景 1.展开函数在多个参数的地方使用 .意指用于函数传参 2.多个元素的地方使用,意指用于数组字面量 3.多个边框的地方使用,意指用于解构赋值 函 ...
- CV3——学习笔记-实战项目(上):如何搭建和训练一个深度学习网络
http://www.mooc.ai/course/353/learn?lessonid=2289&groupId=0#lesson/2289 1.AlexNet, VGGNet, Googl ...
- 总结解决 Android-Studio 编译耗时(好久、太长)问题
首先通过搜索有关Android-Studio 编译耗时(好久.太长)问题的博客,速度确实有所改善. 一.暂时解决 Android-Studio 编译耗时(好久.太长)问题 本文链接:https://b ...
- 各种Android UI开源框架 开源库
各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...
- js 加法
使用Number()函数可以解决这个问题,如下 var c = Number(a) + Number(b) 这样c得出来的解果是3,
- [转]Postgres-XL 10r1英文文档
Postgres-XL 是一个完全满足ACID的.开源的.可方便进行水平扩展的.多租户安全的.基于PostgreSQL的数据库解决方案. Postgres-XL 可非常灵活的应用在各类场景中,比如: ...