传送门:https://www.oj.swust.edu.cn/problem/show/1739
Description
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可
放11 个球。
编程任务:
对于给定的n,计算在n根柱子上最多能放多少个球。
Input
由文件input.txt提供输入数据。文件第1 行有1个正整数n(n<=55),表示柱子数。
Output
程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出到文件
output.txt中。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。
Sample Input
4
Sample Output
11
1 8
2 7 9
3 6 10
4 5 11
这个题就是直接枚举球的个数,在符合条件的两个球之前建一条边(小指向大),然后网络流求出最小路径覆盖,当最小路径覆盖大于n时,比当前球个数少一就是答案,然后就是输出路径。
ps:如果向已经跑完dinic的图再加边或者修改边,再次跑出来的dinic就是新增加的流
代码:
//
#include<bits/stdc++.h>
using namespace std;
const int maxn=5000;
const int inf=0x3f3f3f3f;
struct node{
int v,f,next;
}E[maxn*10];
int n,s,t,tot,maxm,ans;
int head[maxn],depth[maxn],cur[maxn];
void init()
{
tot=-1;
memset(head,-1,sizeof head);
}
void add_edge(int u,int v,int w)
{
tot++;
E[tot]={v,w,head[u]};
head[u]=tot;
}
bool bfs()
{
queue<int>q;
memset(depth, 0, sizeof depth);
depth[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = E[i].next) {
if (E[i].f > 0 && depth[E[i].v] == 0) {
depth[E[i].v] = depth[u] + 1;
q.push(E[i].v);
}
}
}
return (bool)depth[t];
}
int dfs(int u, int flow) {
if (u == t) return flow;
for (int& i = cur[u]; ~i; i = E[i].next) {
if (depth[E[i].v] == depth[u] + 1 && E[i].f > 0) {
int temp = dfs(E[i].v, min(flow, E[i].f));
if (temp > 0) {
E[i].f -= temp;
E[i ^ 1].f += temp;
return temp;
}
}
}
return 0;
}
int dinic() {
int ans = 0;
while (bfs()) {
int temp;
for(int i=0;i<maxn;i++) cur[i]=head[i];
while (temp = dfs(s, inf)) {
ans += temp;
}
}
return ans;
}
inline bool check(int x)
{
return (int(sqrt(x))*int(sqrt(x)))==x;
}
void print(int u)
{
for(int i=head[u];~i;i=E[i].next){
if(E[i].f==0&&E[i].v-maxm!=ans+1){
if(E[i].v==s){
puts("");
return;
}
printf("%d ",E[i].v-maxm);
print(E[i].v-maxm);
}
}
}
int main()
{
scanf("%d",&n);
s=0,t=maxn-1,maxm=maxn/2;
init();
for(int i=1,temp=1;;i++,temp++){
for(int j=1;j<i;j++){
if(check(i+j)){
add_edge(j,i+maxm,1);
add_edge(maxm+i,j,0);
}
}
add_edge(s,i,1);
add_edge(i,s,0);
add_edge(i+maxm,t,1);
add_edge(t,i+maxm,0);
temp-=dinic();
if(temp>n) {
ans=i-1;
break;
}
}
/*init();
for(int i=1;i<=ans;i++){
for(int j=1;j<i;j++){
if(check(i+j)){
add_edge(j,i+maxm,1);
add_edge(maxm+i,j,0);
}
}
add_edge(s,i,1);
add_edge(i,s,0);
add_edge(i+maxm,t,1);
add_edge(t,i+maxm,0);
}
dinic();*/
//这里可以不用重新建边跑网络流,只需要在输出路径时不输出多余的一个球即可
printf("%d\n",ans);
print(t);
system("pause");
return 0;
}