您的位置:首页 > 理论基础 > 计算机网络

线性规划与网络流24题 04魔术球问题

2011-10-04 16:59 288 查看
拆点的另一个方法。。: 把这个点 i 拆成 i<<1 和 ( i<<1) +1; 因为上限无穷所以这样会比较方便点。。。然后就是枚举时候不要二分。。。那样会重新构图的。。。顺序枚举可以用到回退边的流量来恢复构建的图。。。然后结合给的讲解就可以写出来了。。。。

【问题分析】

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

【建模方法】

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

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

【建模分析】

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

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

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define inf 1<<30
#define ext 2000
#define N 200000
#define M 4005
#define cc(m,v) memset(m,v,sizeof(m))
struct node{
int u,v,f,next;
}edge
;
int head[M],p,lev[M],cur[M];
int que
,squ[M],vis[M];
void ainit(){
p=0,cc(head,-1),cc(vis,0),vis[0]=vis[1]=1;
}
void addedge(int u,int v,int f){
edge[p].u=u,edge[p].v=v,edge[p].f=f,edge[p].next=head[u],head[u]=p++;
edge[p].u=v,edge[p].v=u,edge[p].f=0,edge[p].next=head[v],head[v]=p++;
}
bool bfs(int s,int t){
int i,v,u,qin=0,qout=0;
cc(lev,-1),lev[s]=0,que[qin++]=s;
while(qout!=qin){
u=que[qout++];
for(i=head[u];i!=-1;i=edge[i].next)
if(edge[i].f>0 && lev[v=edge[i].v]==-1){
lev[v]=lev[u]+1,que[qin++]=v;
if(v==t) return 1;
}
}
return 0;
}
int dinic(int s,int t){
int i,k,f,u,qin;
int flow=0;
while(bfs(s,t)){
memcpy(cur,head,sizeof(head));
u=s,qin=0;
while(1){
if(u==t){
for(k=0,f=inf;k<qin;k++)
if(edge[que[k]].f < f) f=edge[que[i=k]].f;
for(k=0;k<qin;k++)
edge[que[k]].f-=f,edge[que[k]^1].f+=f;
flow+=f,u=edge[que[qin=i]].u;
}
for(i=cur[u];cur[u]!=-1;i=cur[u]=edge[cur[u]].next)
if(edge[i].f>0 && lev[u]+1==lev[edge[i].v]) break;
if(cur[u]!=-1)
que[qin++]=cur[u],u=edge[cur[u]].v;
else{
if(qin==0) break;
lev[u]=-1,u=edge[que[--qin]].u;
}
}
}
return flow;
}
void dfs(int u){
int v;
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
if(edge[i].f==0 && !vis[v=edge[i].v] && edge[i].v>1){
printf(" %d",(v-1)>>1);
dfs(v-1); return;
}
}
int main(){
int n,i,j,k,s,t,ans;
cc(squ,0);
for(i=1;i*i<M;i++)
squ[i*i]=1;
while(scanf("%d",&n)!=-1){
ainit();
s=0,t=1;
for(i=1;i<M ;i++){
addedge(s,i<<1,1),addedge((i<<1)+1,t,1);
for(j=1;j<i;j++)  if(squ[j+i])
addedge(j<<1,(i<<1)+1,1);
for(k=0;k<p;k+=2)
edge[k].f+=edge[k^1].f,edge[k^1].f=0;
if(i-dinic(s,t)>n) break;
}
printf("%d\n",i-1);
ainit();ans=i-1;
for(i=1;i<=ans;i++){
addedge(s,i<<1,1),addedge((i<<1)+1,t,1);
for(j=1;j<i;j++)  if(squ[j+i])
addedge(j<<1,(i<<1)+1,1);
}
dinic(s,t);
for(j=1;j<=ans;j++)
if(!vis[j<<1]){
printf("%d",j);
dfs(j<<1);
printf("\n");
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: