博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POWOJ 1739: 魔术球问题 DAG最小路径覆盖转最大流
阅读量:5230 次
发布时间:2019-06-14

本文共 3495 字,大约阅读时间需要 11 分钟。

题意:

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。 (1)每次只能在某根柱子的最上面放球。 (2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。 试设计一个算法,计算出在n根柱子上最多能放多少个球。对于给定的n,计算在n根柱子上最多能放多少个球。

tags: 对大佬来说应该是很素的一道题,但某还是花了好多时间才做出来。。 一开始连建图都有点懵,然后最小路径还是新概念,最大匹配也不太懂,最大流倒是会一点。 然后要打印答案,也不会,或者说最小路径该怎么从网络流中反映出来,这没怎么搞懂。

可以参考 ,讲的很详细。

1、首先要想到怎么建图,然后把最小路径覆盖转化为二分图最大匹配,再化为最大流求解。

2、某是二分答案,每次跑一遍网络流,判断最小路径覆盖数是否小于等于柱子数 。  对于i<j,且 i+j是完全平方数, 建二分图,即连接点 i 和点 m+j 。最后1~m点连接源点,m+1~2*m点连接汇点。   在二分图中求出最大匹配数,DAG中的最小路径覆盖数 = DAG图中的节点数 - 相应二分图中的最大匹配数。 最后搜索打印答案。

3、本来二分应该要比枚举快,但这题,枚举可以利用上次的残余网络,而二分每次要重新建图,反而更慢。

建模分析

由于是顺序放球,每根柱子上的球满足这样的特征,即下面的球编号小于上面球的编号。抽象成图论,把每个球看作一个顶点,就是编号较小的顶点向编号较大的顶点连接边,条件是两个球可以相邻,即编号之和为完全平方数。每根柱子看做一条路径,N根柱子要覆盖掉所有点,一个解就是一个路径覆盖。

//  1739: 魔术球问题#include
using namespace std;#pragma comment(linker, "/STACK:102400000,102400000")#define rep(i,a,b) for (int i=a;i<=b;i++)#define per(i,b,a) for (int i=b;i>=a;i--)#define mes(a,b) memset(a,b,sizeof(a))#define INF 0x3f3f3f3ftypedef long long ll;const int N = 100005;int n, tot, dis[N], head[N], cur[N], remain[N<<1], path[N];bool vis[N];struct Edge{
int to, next;}e[N<<1];void Addedge(int u,int v,int w) { e[tot]={v,head[u]}; remain[tot]=w; head[u]=tot++; e[tot]={u,head[v]}; remain[tot]=0; head[v]=tot++; //反向流量初始为 0}bool bfs(int st, int ed) // bfs()查询 s到 t是否还有增广路径{ for(int i=st; i<=ed; i++) cur[i]=head[i]; mes(dis, -1); queue
q; q.push(st); dis[st]=0; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u]; i!=-1; i=e[i].next) { int to=e[i].to; if(dis[to]==-1 && remain[i]) { //remain[]<0 也可 q.push(to); dis[to]=dis[u]+1; if(to==ed) return true; } } } return false;}int dfs(int now, int ed, int flow) // flow表示能够提供给当前结点的最大流量{ if(now==ed || flow==0) return flow; int s, ans=0; for(int &i=cur[now]; i!=-1; i=e[i].next) { // &i=cur[now],改变了cur[now],让下一次的dfs不重复走 int to=e[i].to; if(dis[to]==dis[now]+1 && (s=dfs(to,ed,min(flow,remain[i]))) ) { if(to>(ed-1)/2) path[now]=to-(ed-1)/2, vis[path[now]]=1; // 记录路径 ans+=s, flow-=s; // ans表示当前结点可以消耗的最大流量 remain[i]-=s, remain[i^1]+=s; // 更新残余流量 if(flow==0) break; } } return ans;}int dinic(int m){ int st=0, ed=2*m+1, s, ans=0; while(bfs(st,ed)) while(s=dfs(st,ed,INF)) ans+=s; // ans即是最大流,也是二分图最大匹配数 return ans;}int solve(int m){ int st=0, ed=2*m+1; tot=0; mes(head,-1); mes(vis,false); mes(path, 0); rep(i,1,m) rep(j,i+1,m) { //加边,建二分图 if((int)sqrt(i+j)==sqrt(i+j)) Addedge(i,m+j,1); } rep(i,1,m) { Addedge(st,i,1), Addedge(m+i,ed,1); } return m-dinic(m); // DAG中的最小路径覆盖数 = DAG图中的节点数 - 相应二分图中的最大匹配数 // 二分图中,最小边覆盖 = 图中点的个数 - 最大匹配数=最大独立集 , 最大匹配数 = 左边匹配点 + 右边未匹配点}void print(int ans) // 打印答案{ printf("%d\n", ans); rep(i,1,ans) if(vis[i]==0) { printf("%d", i); int pos=i; while(path[pos]) { printf(" %d", path[pos]); pos=path[pos]; } puts(""); }}int main(){ scanf("%d", &n); int l=n, r=2000, ans=0; while(l<=r) { int mid=(l+r)>>1; int ans1=solve(mid); if(ans1<=n) { // 每次跑一遍网络流,判断最小路径覆盖数是否小于等于柱子数 if(ans1==n) ans=max(ans, mid); l=mid+1; } else r=mid-1; } solve(ans); print(ans); return 0;}
View Code

转载于:https://www.cnblogs.com/sbfhy/p/6680024.html

你可能感兴趣的文章
redis哨兵集群、docker入门
查看>>
hihoCoder 1233 : Boxes(盒子)
查看>>
团队的绩效评估计划
查看>>
oracle中anyData数据类型的使用实例
查看>>
C++对vector里面的元素排序及取任意重叠区间
查看>>
软件测试——性能测试总结
查看>>
12.4站立会议
查看>>
泰勒展开,傅里叶变换,拉普拉斯变换和Z变换的物理意义
查看>>
Java Concurrentmodificationexception异常原因和解决方法
查看>>
Python 面向对象(其四)
查看>>
客户端访问浏览器的流程
查看>>
codeforces水题100道 第二十二题 Codeforces Beta Round #89 (Div. 2) A. String Task (strings)
查看>>
c++||template
查看>>
[BZOJ 5323][Jxoi2018]游戏
查看>>
编程面试的10大算法概念汇总
查看>>
Vue
查看>>
shell脚本图书
查看>>
使用两个不同类型的数据进行加法计算时,使用异常处理语句捕获由于数据类型错误而出现的异常,发生生成错误。是否继续并运行上次的成功生成?...
查看>>
python-三级菜单和购物车程序
查看>>
条件断点 符号断点
查看>>