[luoguP2765] 魔术球问题(最大流—最小不相交路径覆盖)

传送门

枚举球的个数 num

如果 i < j && (i + j) 是完全平方数,那么 i -> j‘ 连一条边

再加一个超级源点 s,s -> i

再加一个超级汇点 t,i‘ -> t

那么当前可以放的柱子的最小数量就是最小不相交路径数

如果当前的最小不相交路径数 > num,break

求最大流的时候别忘了记录方案

——代码

  1 #include <cmath>
  2 #include <queue>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <iostream>
  6 #define N 10001
  7 #define M 200001
  8 #define mid 5000
  9 #define min(x, y) ((x) < (y) ? (x) : (y))
 10
 11 int n, cnt, sum, ans, s, t = mid << 1;
 12 int head[N], to[M], next[M], val[M], suc[N], dis[N];
 13 bool vis[N];
 14
 15 inline int read()
 16 {
 17     int x = 0, f = 1;
 18     char ch = getchar();
 19     for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = -1;
 20     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - ‘0‘;
 21     return x * f;
 22 }
 23
 24 inline void add(int x, int y, int z)
 25 {
 26     to[cnt] = y;
 27     val[cnt] = z;
 28     next[cnt] = head[x];
 29     head[x] = cnt++;
 30 }
 31
 32 inline bool bfs()
 33 {
 34     int i, u, v;
 35     std::queue <int> q;
 36     memset(dis, -1, sizeof(dis));
 37     q.push(s);
 38     dis[s] = 0;
 39     while(!q.empty())
 40     {
 41         u = q.front(), q.pop();
 42         for(i = head[u]; i ^ -1; i = next[i])
 43         {
 44             v = to[i];
 45             if(val[i] && dis[v] == -1)
 46             {
 47                 dis[v] = dis[u] + 1;
 48                 if(v == t) return 1;
 49                 q.push(v);
 50             }
 51         }
 52     }
 53     return 0;
 54 }
 55
 56 inline int dfs(int u, int maxflow)
 57 {
 58     if(u == t) return maxflow;
 59     int i, v, d, ret = 0;
 60     for(i = head[u]; i ^ -1; i = next[i])
 61     {
 62         v = to[i];
 63         if(val[i] && dis[v] == dis[u] + 1)
 64         {
 65             d = dfs(v, min(val[i], maxflow));
 66             ret += d;
 67             val[i] -= d;
 68             val[i ^ 1] += d;
 69             if(d) suc[u] = v - mid;
 70             if(ret == maxflow) return ret;
 71         }
 72     }
 73     return ret;
 74 }
 75
 76 int main()
 77 {
 78     int i, j, now, num = 0;
 79     n = read();
 80     memset(head, -1, sizeof(head));
 81     while(1)
 82     {
 83         num++;
 84         add(s, num, 1), add(num, s, 0);
 85         add(num + mid, t, 1), add(t, num + mid, 0);
 86         for(i = 1; i < num; i++)
 87             if(sqrt(i + num) == (int)sqrt(i + num))
 88                 add(i, num + mid, 1), add(num + mid, i, 0);
 89         while(bfs()) sum += dfs(s, 1e9);
 90         if(num - sum > n) break;
 91     }
 92     cnt = 0;
 93     memset(head, -1, sizeof(head));
 94     for(i = 1; i < num; i++)
 95     {
 96         add(s, i, 1), add(i, s, 0);
 97         add(i + mid, t, 1), add(t, i + mid, 0);
 98     }
 99     for(i = 1; i < num; i++)
100         for(j = 1; j < i; j++)
101             if(sqrt(i + j) == (int)sqrt(i + j))
102                 add(j, i + mid, 1), add(i + mid, j, 0);
103     while(bfs()) dfs(s, 1e9);
104     printf("%d\n", num - 1);
105     for(i = 1; i < num; i++)
106         if(!vis[i])
107         {
108             now = i;
109             while(now)
110             {
111                 printf("%d ", now);
112                 vis[now] = 1;
113                 now = suc[now];
114             }
115             puts("");
116         }
117     return 0;
118 }

时间: 2024-11-05 13:44:26

[luoguP2765] 魔术球问题(最大流—最小不相交路径覆盖)的相关文章

【网络流24题】魔术球问题(最小不相交路径覆盖)

[网络流24题]魔术球问题 2014年3月7日3,5344 Description 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,4的球.(1)每次只能在某根柱子的最上面放球.(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数.试设计一个算法,计算出在n根柱子上最多能放多少个球.例如,在4 根柱子上最多可放11 个球. 编程任务:对于给定的n,计算在n根柱子上最多能放多少个球. Input Format 文件第1 行有1个正整数n,表示柱子数. Output Fo

HDU 4862 Jump 最小k路径覆盖 费用流

gg... 题意: 给定n*m的矩阵 选<=k个起点 每个起点可以向右或向下跳任意步 花费是2点间的曼哈顿距离 若2个格子的数字一样 则赚取格子上的数字的价值 问:遍历整个图的最小花费 若不能遍历则输出-1 #include <stdio.h> #include <string.h> #include <iostream> #include <math.h> #include <queue> #include <set> #in

HDU 4862 Jump (2014-多校1-1002,最小K路径覆盖,最小费用最大流)

题目: http://acm.hdu.edu.cn/showproblem.php?pid=4862 题意: 给你一个n*m的矩阵,填充着0-9的数字,每次能从一个点出发,到它的右边或者下边的点,花费为|x1-x2|+|y1-y2|-1,如果跳跃的起点和终点的数字相同,则获得这个数字的收益,不能走已经走过的点 有K次重新选择起点的机会 如果可以走遍所有点,则输出最大的价值(收益-花费) 否则,输出-1 方法: 最小K路径覆盖,最小费用最大流 建图: 每个点拆为2点:X部和Y部,(a,b)表示流量

HDU 4862 Jump (最小K路径覆盖)

HDU 4862 Jump 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4862 题意:给定一个N*M的矩阵,矩阵里面为0~9的数字.现在规定从一个点可以跳到它正下方和正右方的点,花费的费用为曼哈顿距离 - 1.如果在跳的过程中,两个点的数字相同,那么将得到该点的数字.规定可以从任意点开始跳,每个点只能经过1次.最多可以选择K个点来作为起点进行跳跃.问能否经过所有的点,如果可以,那么花费的费用是多少. 思路: 如果是最小路径覆盖,那么很容易构造图.但

hdu 4862 KM算法 最小K路径覆盖的模型

http://acm.hdu.edu.cn/showproblem.php?pid=4862 选t<=k次,t条路要经过所有的点一次并且仅仅一次, 建图是问题: 我自己最初就把n*m 个点分别放入X集合以及Y集合,再求最优匹配,然后连样例都过不了,而且其实当时解释不了什么情况下不能得到结果,因为k此这个条件相当于没用上... 建图方法: 1.X集合和Y集合都放入n*m+k个点,X中前n*m个点和Y中前n*m个点之间,如果格子里的值相等,权就是(收益-耗费),不等就是(-耗费),因为要的是最大收益

HDU 4862 Jump(最小K路径覆盖)

输入一个n×m网格图,每个结点的值为0-9,可以从任意点出发不超过k次,走完每个点且仅访问每个结点一次,问最终的能量最大值.不可全部走完的情况输出-1. 初始能量为0. 而结点(x,y)可以跳跃到结点(x,y+dy)或(x+dx,y).消耗能量为跳跃前后结点的曼哈顿距离 - 1 .若跳跃前后的结点的值相等,能量加上那个值. 具体建图可以参考这里http://blog.sina.com.cn/s/blog_6bddecdc0102uy9g.html 最小K路径覆盖其实在之前是见过的打过的,不过这次

网络流24题之最小路径覆盖问题

DAG的最小不相交路径覆盖 算法:把原图的每个点V拆成Vx 和Vy两个点,如果有一条有向边A->B,那么就加边Ax−>By .这样就得到了一个二分图.那么最小路径覆盖=原图的结点数-新图的最大匹配数. 证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径.我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1.所以找到了几条匹配边,路径数就减少了多少.所以有最小路径覆盖=原图的结点数-新图的最大匹配数. 因为路径之间不能有公共点,所以加的边之间也不能有

有向无环图(DAG)的最小路径覆盖

DAG的最小路径覆盖 定义:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点. 最小路径覆盖分为最小不相交路径覆盖和最小可相交路径覆盖. 最小不相交路径覆盖:每一条路径经过的顶点各不相同.如图,其最小路径覆盖数为3.即1->3>4,2,5. 最小可相交路径覆盖:每一条路径经过的顶点可以相同.如果其最小路径覆盖数为2.即1->3->4,2->3>5. 特别的,每个点自己也可以称为是路径覆盖,只不过路径的长度是0. DAG的最小不相交路径覆盖 算法:把原图的每个点

luogu P2764 最小路径覆盖问题

DAG的最小不相交路径覆盖板子 #include<bits/stdc++.h> using namespace std; int ans=0,len=0,lin[500],din[50000],dout[50000],level[500],x,y,S,T,n,m,q[500]; int q1[500],q2[500],tail1=0,tail2=0; bool f[500]; struct one { int x,y,v,next,reverse; }; one e[30000]; void