网络流24题-4

来源:互联网 发布:织梦cms百科 编辑:程序博客网 时间:2024/06/13 18:36

魔术球问题

«问题描述:
假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,…….的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多可
放 11 个球。
«编程任务:
对于给定的 n,计算在 n 根柱子上最多能放多少个球。
«数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示柱子数。
«结果输出:
程序运行结束时,将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件
output.txt 中。文件的第一行是球数。接下来的 n 行,每行是一根柱子上的球的编号。
输入文件示例
input.txt
4
输出文件示例
output.txt
11
1 8
2 7 9
3 6 10
4 5 11

【问题分析】

枚举答案转化为判定性问题,然后最小路径覆盖,可以转化成二分图最大匹配,从而用最大流解决。

【建模方法】

枚举答案A,在图中建立节点1..A。如果对于i < j有i+j为一个完全平方数,连接一条有向边(i,j)。该图是有向无环图,求最小路径覆盖。如果刚好满足最小路径覆盖数等于N,那么A是一个可行解,在所有可行解中找到最大的A,即为最优解。

具体方法可以顺序枚举A的值,当最小路径覆盖数刚好大于N时终止,A-1就是最优解。

【建模分析】

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

最小路径覆盖数随球的数量递增不递减,满足单调性,所以可以枚举答案(或二分答案),对于特定的答案求出最小路径覆盖数,一个可行解就是最小路径覆盖数等于N的答案,求出最大的可行解就是最优解。本问题更适合枚举答案而不是二分答案,因为如果顺序枚举答案,每次只需要在残量网络上增加新的节点和边,再增广一次即可。如果二分答案,就需要每次重新建图,大大增加了时间复杂度。

—byvoid
个人理解:最多能放多少不知道,所以需要枚举答案,但答案具有单调性质,能放N个绝对能放 < N个。二分答案。顺序枚举也可以。建图,1~N,如果有i < j(i+j为完全平方数链接)如果刚好满足就是可以,最小路径覆盖数为N,一个柱子起代表的就是一个路径。但二分答案无法利用上次的残余网络结果所以枚举更快一点。

#include<iostream>#include<math.h>#include<cstring>#include<algorithm>#include<set>#include<map>#include<queue>#include<cstdio>#include<vector>const int INF=0x7fffffff;using namespace std;const int maxn=4001;struct edge{    int to,cap,rev;};vector<edge> G[maxn];int level[maxn],match[maxn],path[maxn],iter[maxn];bool book[maxn],issquare[maxn];int N,s=0,OFFSET=2000,t=maxn-1,Ans,maxflow;void add_edge(int from,int to,int cap){    G[from].push_back((edge){to,cap,G[to].size()});    G[to].push_back((edge){from,0,G[from].size()-1});}void init(){    cin>>N;    for(int i=1;i<=60;i++)        issquare[i*i]=true;}void bfs(int s){    memset(level,-1,sizeof(level));    queue<int > que;    level[s]=0;    que.push(s);    while(!que.empty())    {        int v=que.front();        que.pop();        for(int i=0;i<G[v].size();i++)        {            edge &e=G[v][i];            if(e.cap&&level[e.to]<0)            {                level[e.to]=level[v]+1;                que.push(e.to);            }        }    }}int dfs(int v,int t,int f){    if(v==t)        return f;    for(int &i=iter[v];i<G[v].size();i++)    {        edge &e=G[v][i];        if(e.cap&&level[e.to]>level[v])        {            int d=dfs(e.to,t,min(e.cap,f));            if(d>0)            {                e.cap-=d;                G[e.to][e.rev].cap+=d;                return d;            }        }    }    return 0;}int max_flow(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)            return flow;        memset(iter,0,sizeof(iter));        int f=0;        while((f=dfs(s,t,INF))>0)            flow+=f;    }}void solve(){    int i,j;    add_edge(s,1,1);    add_edge(1+OFFSET,t,1);    maxflow+=max_flow(s,t);    for(i=1;i-maxflow<=N;)    {        i++;        add_edge(s,i,1);        add_edge(i+OFFSET,t,1);        for(j=1;j<i;j++)            if(issquare[i+j])                add_edge(j,i+OFFSET,1);        maxflow+=max_flow(s,t);    }    Ans=i-1;    for(int i=0;i<=Ans;i++)        G[i].clear();    G[t].clear();    for(int i=1;i<=Ans;i++)    {        add_edge(s,i,1);        add_edge(i+OFFSET,t,1);        for(int j=1;j<i;j++)            if(issquare[i+j])                add_edge(j,i+OFFSET,1);    }    maxflow=max_flow(s,t);}void print(){    cout<<Ans<<endl;    int i,j,p;    for(i=Ans+OFFSET;i>OFFSET;i--)    {        for(j=0;j<G[i].size();j++)        {            edge &e=G[i][j];            if(e.cap==1)             {                match[e.to]=i-OFFSET;                break;             }        }    }    memset(book,false,sizeof(book));    for(i=1;i<=Ans;i++)    {        p=0;        if(book[i])            continue;        for(j=i;j;j=match[j])        {            path[++p]=j;            book[j]=true;        }        for(j=1;j<p;j++)            cout<<path[j]<<" ";        cout<<path[j]<<endl;    }}int main(){    init();    solve();    print();    return 0;}