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 ...
随机推荐
- P2320 [HNOI2006]鬼谷子的钱袋——进制(没事就别看这个了)
就是n可以被1到n/2的所有数表示出来: 我一开始写了个把二进制数里的1拿出来,但是WA了两个点: 分治? 好多人说数据有问题,我也不知道,也不想知道: %:include<cstdio> ...
- hadoop 2.x HA 出现ssh不能解析问题记录。
在docker里面安装hadoop HA 在启动或者停止的时候报ssh不能解析问题. 问题现象: 发现图片不清晰:把问题现象粘贴如下: root@master:/usr/local/hadoop-2. ...
- Java枚举知识点
近几天从单例模式及阿里开发手册中遇到枚举,之前没怎么关注过. 便学习一下,此次看了多方资料,并写Demo实现,记录下知识点,方便之后巩固. 枚举的两个优点: 1. 保证了类型安全:调用者无法随意传一个 ...
- Linux中系统状态检测命令
1.ifconfig用于获取网卡配置与网络状态等信息,格式为:ifconfig [网络设备] [参数] 2.uname命令用于查看系统内核版本等信息,格式为:uname [-a] 查看系统的内核名称. ...
- 临界区代码 critical section Locks and critical sections in multiple threads
临界区 在同步的程序设计中,临界区段(Critical section)指的是一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源有无法同时被多个线程访问的特性. 当有线程进入临 ...
- gis空间分析案例_7参数单坐标转换
gis空间分析案例_7参数单坐标转换 商务科技合作:向日葵,135-4855__4328,xiexiaokui#qq.com 功能: 对输入的单个坐标,利用7参数,一步进行坐标变换,使用极为直观,极大 ...
- phpstorm设置最多标签数
在settings=>Editor=>General=>Editor Tabs的Tab limit下可以设置当前窗口能够打开的最多标签数目,默认是10,超过设定值时,会自动关闭前面的 ...
- 数据库info.sql
/* Navicat MySQL Data Transfer Source Server : loaderman Source Server Version : 50540 Source Host : ...
- Bitmap添加水印效果
package com.loaderman.customviewdemo; import android.app.Activity; import android.graphics.Bitmap; i ...
- 常规函数模块CALL in new task 报错
使用START NEW TASK, 函数需要是远程调用模块. 错误:FUNCTION module ' ZMMFM0021' cannot be used for 'remote' CALLS. ...