【题解】Inspection UVa 1440 LA 4597 NEERC 2009
题目传送门:https://vjudge.net/problem/UVA-1440
看上去很像DAG的最小路径覆盖QwQ?
反正我是写了一个上下界网络流,建模方法清晰易懂。
建立源$s$,向每个原图中的点连边,下界为$0$,上界为$\infty$,表示在每个点可以放置无限多的人。
建立汇$t$,每个原图中的点向汇连边,下界为$0$,上界为$\infty$,表示人可以在任意一个点停止滑雪。
对于原图中的每条弧$<u,v>$,连边$<u,v>$,下界为$1$,上界为$\infty$,表示这条路至少要被检查一遍。
然后跑一个有源汇上下界最小流就好啦OvO。
不会求上下界网络流的看这里:http://www.cnblogs.com/mlystdcall/p/6734852.html
输出方案?瞎搞QwQ。任选一个“需要放置人的点”开始dfs,在每个点的出边中任选一个“还需要被访问的”继续dfs,详见代码和注释。
这样输出方案为什么是对的?时间太久远辣我已经忘辣QwQ。
代码如下:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue> using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = ; namespace ISAP {
const int MAXV = MAXN;
const int MAXE = ( MAXV*MAXV/ + MAXV* )*; struct Edge {
int u, v, c, f;
Edge(){}
Edge( int u, int v, int c, int f ):
u(u),v(v),c(c),f(f){}
}edge[MAXE<<];
int n, m, s, t, ss, tt;
int head[MAXV], nxt[MAXE<<], eid[MAXE<<], eidx;
void init( int n2, int ss2, int tt2 ) { // 初始化,设置附加源和附加汇
n = n2; ss = ss2; tt = tt2;
m = eidx = ;
memset( head, -, sizeof(head) );
}
int adde( int u, int v, int c ) { // 添加一条只有上界的边
int rtn = m;
eid[eidx] = m; nxt[eidx] = head[u]; head[u] = eidx++;
edge[m++] = Edge(u,v,c,);
eid[eidx] = m; nxt[eidx] = head[v]; head[v] = eidx++;
edge[m++] = Edge(v,u,,);
return rtn;
}
int adde2( int u, int v, int b, int c ) { // 添加一条有上下界的边,返回边的下标
int rtn = adde(u,v,c-b);
adde(ss,v,b);
adde(u,tt,b);
return rtn;
}
// 以下ISAP板子
int prev[MAXV], dist[MAXV], num[MAXV], cur[MAXV], res[MAXV];
queue<int> bfsq;
void bfs() {
for( int i = ; i <= n; ++i ) dist[i] = n;
dist[t] = ; bfsq.push(t);
while( !bfsq.empty() ) {
int u = bfsq.front(); bfsq.pop();
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[eid[i]];
if( dist[e.v] == n ) {
dist[e.v] = dist[u] + ;
bfsq.push(e.v);
}
}
}
}
void augment() {
int u = t, flow = res[t];
while( u != s ) {
int i = prev[u];
edge[i].f += flow;
edge[i^].f -= flow;
u = edge[i].u;
}
}
bool advance( int &u ) {
for( int i = cur[u]; ~i; i = nxt[i] ) {
Edge &e = edge[eid[i]];
if( e.c > e.f && dist[e.v] + == dist[u] ) {
prev[e.v] = cur[u] = i;
res[e.v] = min( res[u], e.c - e.f );
u = e.v;
return true;
}
}
return false;
}
bool retreat( int &u ) {
if( --num[dist[u]] == ) return false;
int newd = n;
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[eid[i]];
if( e.c > e.f ) newd = min( newd, dist[e.v] + );
}
++num[ dist[u] = newd ];
cur[u] = head[u];
if( u != s ) u = edge[prev[u]].u;
return true;
}
int solve( int s2, int t2 ) { // 以s2为源,t2为汇跑最大流
s = s2; t = t2;
bfs();
for( int i = ; i <= n; ++i )
cur[i] = head[i], ++num[dist[i]];
int u = s, flow = ;
res[s] = INF;
while( dist[s] < n ) {
if( u == t ) {
augment();
flow += res[t];
u = s;
}
if( !advance(u) )
if( !retreat(u) )
break;
}
return flow;
}
} int n, s, t, ss, tt; // 点的个数,源,汇,附加源,附加汇 namespace Solve {
using ISAP::head;
using ISAP::nxt;
using ISAP::eid;
using ISAP::Edge;
using ISAP::edge;
bool first;
void dfs( int u ) { // dfs输出方案
// printf( "Debug: u = %d\n", u );
if( !first ) putchar(' ');
first = false;
printf( "%d", u );
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[eid[i]];
if( e.v <= n && e.f > ) { // 任选一条边走下去
// printf( "going eid = %d, from %d to %d, flow_left = %d\n", eid[i], e.u, e.v, e.f );
--e.f;
dfs(e.v);
return;
}
}
}
void addbound() { // 把每条边流量加上下界,恢复成原图的样子,方便输出方案
using ISAP::m;
for( int i = ; i < m; ++i ) {
Edge &e = edge[eid[i]];
if( e.u <= n && e.v <= n && e.c > )
++e.f;
}
}
} namespace Debug { // 调试用QwQ
void print_flow() {
using ISAP::edge;
using ISAP::Edge;
using ISAP::eid;
using ISAP::m;
for( int i = ; i < m; ++i ) {
Edge &e = edge[eid[i]];
if( e.u <= n && e.v <= n && e.c > )
printf( "eid = %d, from %d to %d, flow = %d\n", eid[i], e.u, e.v, e.f );
}
}
void print_flow2() {
using ISAP::edge;
using ISAP::Edge;
using ISAP::eid;
using ISAP::m;
for( int i = ; i < m; ++i ) {
Edge &e = edge[eid[i]];
if( e.f > )
printf( "eid = %d, from %d to %d, flow = %d\n", eid[i], e.u, e.v, e.f );
}
}
} int main() {
while( scanf( "%d", &n ) == ) {
s = n+, t = n+, ss = n+, tt = n+;
ISAP::init(tt,ss,tt);
for( int i = ; i <= n; ++i ) {
int mi; scanf( "%d", &mi );
while( mi-- ) {
int v; scanf( "%d", &v );
ISAP::adde2(i,v,,INF);
}
ISAP::adde2(s,i,,INF);
ISAP::adde2(i,t,,INF);
}
int flow1 = ISAP::solve(ss,tt);
// printf( "flow1 = %d\n", flow1 );
// Debug::print_flow();
// Debug::print_flow2();
int tsedge = ISAP::adde2(t,s,,INF); // 存储弧<t,s>的信息,调试用QwQ
int ans = ISAP::solve(ss,tt);
// printf( "t_s flow = %d\n", ISAP::edge[tsedge].f );
// Debug::print_flow();
// Debug::print_flow2();
printf( "%d\n", ans );
Solve::addbound(); // 把每条图中的边流量加上下界,恢复成原图的样子,方便输出方案
while( ans ) {
using namespace Solve;
for( int i = head[s]; ~i; i = nxt[i] ) {
Edge &e = edge[eid[i]];
if( e.v <= n && e.f > ) { // 任选一个点dfs,输出方案
first = true;
--e.f;
--ans;
dfs(e.v);
putchar('\n');
}
}
}
}
return ;
}
【题解】Inspection UVa 1440 LA 4597 NEERC 2009的更多相关文章
- uva 1440 & uvalive 4597
题目链接 题意: DAG的最小路径覆盖,一条边可以被重复覆盖多次,但是一次只能沿着DAG的方向覆盖一条链,问最少覆盖次数. 思路: 看了半天没有思路,所以去搜索了题解,然后发现是有源汇上下界的最小流, ...
- 题解 P1951 【收费站_NOI导刊2009提高(2)】
查看原题请戳这里 核心思路 题目让求最大费用的最小值,很显然这道题可以二分,于是我们可以二分花费的最大值. check函数 那么,我们该怎么写check函数呢? 我们可以删去费用大于mid的点以及与其 ...
- 【题解】Casting Spells LA 4975 UVa 1470 双倍回文 SDOI 2011 BZOJ 2342 Manacher
首先要吐槽LRJ,书上给的算法标签是“有难度,需要结合其他数据结构”,学完Manacher才发现几乎一裸题 题目的意思是问原串中有多少个wwRwwR这样的子串,其中wR表示w的反串 比较容易看出来,w ...
- UVa 1440:Inspection(带下界的最小流)***
https://vjudge.net/problem/UVA-1440 题意:给出一个图,要求每条边都必须至少走一次,问最少需要一笔画多少次. 思路:看了好久才勉强看懂模板.良心推荐:学习地址. 看完 ...
- UVA - 1401 | LA 3942 - Remember the Word(dp+trie)
https://vjudge.net/problem/UVA-1401 题意 给出S个不同的单词作为字典,还有一个长度最长为3e5的字符串.求有多少种方案可以把这个字符串分解为字典中的单词. 分析 首 ...
- 题解 【Uva】硬币问题
[Uva]硬币问题 Description 有n种硬币,面值分别为v1, v2, ..., vn,每种都有无限多.给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值 ...
- UVA 12097 LA 3635 Pie(二分法)
Pie My birthday is coming up and traditionally I'm serving pie. Not just one pie, no, I have a numbe ...
- UVA题解三
UVA题解三 UVA 127 题目描述:\(52\)张扑克牌排成一列,如果一张牌的花色或者数字与左边第一列的最上面的牌相同,则将这张牌移到左边第一列的最上面,如果一张牌的花色或者数字与左边第三列的最上 ...
- UVA题解二
UVA题解二 UVA 110 题目描述:输出一个Pascal程序,该程序能读入不多于\(8\)个数,并输出从小到大排好序后的数.注意:该程序只能用读入语句,输出语句,if语句. solution 模仿 ...
随机推荐
- 在Windows2008下添加iscsi存储出现磁盘Offine(The disk is offine because of policy set by an adminstrator)的解决方法
打开CMD命令行输入如下命令: DISKPART.EXE DISKPART> san SAN Policy : Offline Shared DISKPART> san policy=On ...
- sshpass 指定密码远程 ssh 到服务器 或者 scp 发送文件到服务器
在操作linux时,虽然可以对linux配置免秘钥登录,但是在配置免密码登录之前,是需要登录到其他节点主机的,这里提供一种类似ssh的方式,可以在命令后面加上相应的参数来设置你将要登录的远程主机的密码 ...
- CF 1008B Turn the Rectangles(水题+贪心)
There are n rectangles in a row. You can either turn each rectangle by 90 degrees or leave it as it ...
- Scrum立会报告+燃尽图(十月十六日总第七次):总结工作经验,商讨未来策略
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2197 Scrum立会master:李文涛 一.小组介绍 组长:付佳 组员 ...
- 2018软工实践—Alpha冲刺(6)
队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 测试服务器并行能力 学习MSI.CUDA ...
- web登录密码加密
文章:如何实现登录页面密码加密 文章:用RSA加密实现Web登录密码加密传输 文章:web登录用户名密码加密 知乎文章:Web前端密码加密是否有意义? 文章:记录一次黑客模拟攻击 成功拿到淘宝账号和密 ...
- 浅谈 Sql Server 触发器
一.触发器概念 1.1.触发器特征 1.1.1.触发器是在对表进行增.删.改时,自动执行的存储过程.触发器常用于强制业务规则,它是一种高级约束,通过事件进行触发而被执行. ...
- 1014C程序语法树
程序:冒泡算法C程序 #include <stdio.h> main() { int i,j,temp; int a[10]; for(i=0;i<10;i++) scanf (&q ...
- C关键字volatile总结
做嵌入式C开发的相信都使用过一个关键字volatile,特别是做底层开发的.假设一个GPIO的数据寄存器地址是0x50000004,我们一般会定义一个这样的宏: #define GDATA *((vo ...
- C#和Java访问修饰符的比较
访问修饰符对于C#:类 的默认修饰符是 internal(外部类只能被public / internal 修饰)枚举 的默认修饰符是 public 且此类型不允许其它访问修饰符接口 的默认修饰符是 i ...