问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球。 (1)每次只能在某根柱子的最上面放球。 (2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。 试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可 放11个球。 ´编程任务: 对于给定的n,计算在 n根柱子上最多能放多少个球。
´数据输入: 文件第1 行有 1个正整数n,表示柱子数。 ´结果输出: 文件的第一行是球数。
数据规模
n<=60 保证答案小于1600
输入文件示例
4
输出文件示例
11
方案如下
1 8 2 7 9 3 6 10 4 5 11
每一行表示一个柱子上的球
在上一次的残余网络建新边-》(由于从[1~...]按顺序+反向边)-》得新的流
就是编号较小的顶点向编号较大的顶点连接边(皆可),条件是两个球可以相邻,即编号之和为完全平方数。每根柱子看做一条路径,N根柱子要覆盖掉所有点,一个解就是一个路径覆盖。
最小路径覆盖数随球的数量递增不递减,满足单调性,所以可以枚举答案(或二分答案),对于特定的答案求出最小路径覆盖数,一个可行解就是最小路径覆盖数等于N的答案,求出最大的可行解就是最优解。本问题更适合枚举答案而不是二分答案,因为如果顺序枚举答案,每次只需要在残量网络上增加新的节点和边,再增广一次即可。如果二分答案,就需要每次重新建图,大大增加了时间复杂度。
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<queue> #include<map> #include<cstdlib> #include<algorithm> #define V 10605 #define mod 1000000007 #define LL long long using namespace std; int n,m,T,S; int ls[V],pre[V],dep[V],q[V]; struct da { int to,next,dis; }Edge[V*15]; int head[V],tot,ans,s; inline void add(int x,int y,int zz) { Edge[tot].to=y; Edge[tot].dis=zz; Edge[tot].next=head[x]; head[x]=tot++; Edge[tot].to=x; Edge[tot].dis=0; Edge[tot].next=head[y]; head[y]=tot++; } bool Bfs() { // bfs建立层次图 memset(dep, 0, sizeof(dep)); int hd,tl; hd = tl = 0; q[++ tl] = S, dep[S] = 1; while(hd<tl) { int op = q[++hd]; for(int i = head[op] ; i != -1 ; i = Edge[i].next) { if(Edge[i].dis&&(!dep[Edge[i].to])) { dep[Edge[i].to] = dep[op]+1; q[++ tl] = Edge[i].to; if(Edge[i].to==T) return true; } // 遍历到T就返回,此时该层次图已建好 } // 再访问其它的点没有必要 } return false; } int Dfs(int op, int fw) { if(op==T) return fw; int tmp =fw,k; for(int i = head[op] ; i != -1 ; i = Edge[i].next) { if(Edge[i].dis&&tmp&&dep[Edge[i].to]==dep[op]+1) { k = Dfs(Edge[i].to, min(Edge[i].dis, tmp)); if(!k) { //该点后面没有路径,所以要从层次图中删去 dep[Edge[i].to] = 0; // 因为和当前点op层次相同的点 continue; // 还可能会访问到它 } Edge[i].dis-= k, Edge[i^1].dis+= k,tmp-= k; } } return fw-tmp; } int solve() { int i,flow=0; while(Bfs()) { ans-=Dfs(S,mod); } return flow; } inline void init() { tot=0; memset(head,-1,sizeof(head)); } int vis[V]; inline int haha() { // freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout); freopen("balla.in","r",stdin); freopen("balla.out","w",stdout); memset(head,-1,sizeof(head)); cin>>n; T=2*1600+10; while(ans<=n) { ans++;s++; for(int i=1;i<s;i++) if(sqrt(i+s)==(int)sqrt(i+s)) add(s,i+1600,1); add(0,s,1);add(s+1600,T,1); solve(); } //return 0; printf("%d",s-1);//while(1); return 0; /*int sd=s-1,x; while(1) { for(int i=s-1;i>=1;i--) { cout<<i<<endl; if(!vis[i]) { vis[i]=1; for(int j=head[i];j!=-1;j=Edge[j].next) { if(!Edge[j].dis) { //x=1; sd--; ls[Edge[j].to]=i; i=Edge[j].to+1; break; } } } } if(sd==1)break; //cout<<" ER"<<endl; } */ return 0; } int gg=haha(); int main() {;}