题目:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
分析:
枚举球数x,在图中建立节点1…x。如果 i < j,且 i+j是完全平方数,那么就建立一条 i 向 j 的边。然后问题可转化成求这些球的最小路径覆盖数,拆点然后连边跑最大流即可。
这个题不要二分答案,虽然二分答案比枚举效率高,但是在建图层面上,如果是枚举答案,那么在新增加一个球的时候,只需要新增加若干条和这个球相关联边,然后在上次残留网络的基础上看能否再找到增广路即可。二分答案的话需要重新建图。
保存路径可以在dfs的过程中保存下来。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int MAXN = 50005;
const int MAXM = 120005;
const int INF = 0x3f3f3f3f;
struct Edge {
int to, next, cap, flow;
} edge[MAXM];
int tot, head[MAXN];
int Q[MAXN], cur[MAXN], dep[MAXN];
int n, m, S, T, N, to[MAXN],mark[MAXN],maxflow;
void init() {
tot = 2;
memset(head, -1, sizeof head);
memset(to, -1, sizeof to);
memset(mark,0,sizeof mark);
}
void addedge(int u, int v, int w, int rw = 0) {
edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0;
edge[tot].next = head[u]; head[u] = tot++;
edge[tot].to = u; edge[tot].cap = rw; edge[tot].flow = 0;
edge[tot].next = head[v]; head[v] = tot++;
}
bool bfs(int s, int t, int n) {
int Front = 0, tail = 0;
memset(dep, -1, sizeof(dep[0])*(n+1));
dep[s] = 0;
Q[tail++] = s;
while (Front < tail) {
int u = Q[Front++];
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].cap > edge[i].flow && dep[v] == -1) {
dep[v] = dep[u] + 1;
if (v == t) return true;
Q[tail++] = v;
}
}
}
return false;
}
int dfs(int u, int f) {
if (u == T) return f;
int used = 0, rflow = 0;
for (int i = cur[u]; i != -1; i = edge[i].next) {
cur[u] = i;
int v = edge[i].to, w = edge[i].cap - edge[i].flow;
if (w > 0 && dep[v] == dep[u] + 1) {
if ((rflow = dfs(v, min(w, f - used)))) {
to[u] = v;
if(v>5000) mark[v-5000]=1;
used += rflow;
edge[i].flow += rflow; edge[i ^ 1].flow -= rflow;
if (used == f) break;
}
}
}
if (!used) dep[u] = -1;
return used;
}
void dinic(int s, int t, int n) {
while (bfs(s, t, n)) {
for (int i = 0; i <= n; i++) cur[i] = head[i];
maxflow += dfs(s, INF);
}
}
bool is(int x,int y){
int t = x + y;
return t == (int)sqrt(t) * (int)sqrt(t);
}
int main(){
ios::sync_with_stdio(false);
freopen("1.txt","r",stdin);
cin >> n;
init();
maxflow = 0;
S = 0, T = 10005, N = 10005;
int ans;
for(int i=1;;i++){
for(int j=1;j<i;j++){
if(is(i,j)){
addedge(j,i+5000,1);
}
}
addedge(S,i,1), addedge(5000+i,T,1);
dinic(S,T,N);
if(i-maxflow > n){
ans = i;
break;
}
}
ans--;
cout << ans << '\n';
for(int i=1;i<=ans;i++){
if(mark[i]) continue;
int k = i;
cout << k;
while(to[k]!=-1){
cout << " " << to[k]-5000;
k = to[k]-5000;
}
cout << '\n';
}
return 0;
}