网络流24题——魔术球问题(有向无环图最小路径覆盖)

链接:https://www.oj.swust.edu.cn/oj/problem/show/1739

分析:

       有向无环图最小路径覆盖:给定有向无环图,求一个路径划分,使得每个点在且只在一条路径上,路径数要求最少。 对于这种问题,先把每个点看做一条路径,然后连边,每个点最多连出去一条,也最多被连一次,每有一个点被连,路径数-1,所有新增源汇点,连向所有的点,容量为1,然后拆点,保证每个点只过一次,求一下最大流flow,答案为n-flow。

       先考虑对于m个球,至少需要几个柱子。若a+b为平方数且a<b,从a向b连一条边,然后求一下整个图的最小路径覆盖,枚举球数,直到最小路径覆盖大于n。有两个地方需要优化一下:一个是每次求最大流后保留,加球后接着上次的增广;一个是对于n比较大的情况,可以设定一个值k,当m大于k时才求最大流,否则直接下一次加球。

  1 #include<iostream>
  2 #include<vector>
  3 #include<queue>
  4 #include<stack>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<algorithm>
  8 using namespace std;
  9 const int maxn=5e3+5,INF=1e9;
 10 const int maxball=1600;
 11 int idx=0,n;
 12 bool have[maxball+5];
 13 struct Pair{
 14     int x,y;
 15     Pair(int x,int y){
 16         this->x=x;this->y=y;
 17     }
 18 };
 19 bool Cmp(Pair a,Pair b){return a.y<b.y;}
 20 vector<Pair> p;
 21 struct Edge{
 22     int from,to,cap,flow;
 23     Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
 24 };
 25 struct ISAP{
 26     int n,s,t,flow;
 27     vector<Edge> edges;
 28     vector<int> G[maxn];
 29     bool vis[maxn];
 30     int d[maxn];
 31     int cur[maxn];
 32     int p[maxn];
 33     int num[maxn];
 34 
 35     void init(int n){
 36         this->n=n;
 37         flow=0;
 38         edges.clear();
 39         for(int i=0;i<n;i++)G[i].clear();
 40     }
 41 
 42     void AddEdge(int from,int to,int cap){
 43         edges.push_back(Edge(from,to,cap,0));
 44         edges.push_back(Edge(to,from,0,0));
 45         G[from].push_back(edges.size()-2);
 46         G[to].push_back(edges.size()-1);
 47     }
 48 
 49     void BFS(){
 50         memset(vis,0,sizeof(vis));
 51         queue<int> Q;
 52         Q.push(t);
 53         d[t]=0;
 54         vis[t]=1;
 55         while(!Q.empty()){
 56             int x=Q.front();Q.pop();
 57             for(int i=0;i<G[x].size();i++){
 58                 Edge &e=edges[G[x][i]^1];
 59                 if(e.flow==e.cap)continue;
 60                 if(!vis[e.from]){
 61                     vis[e.from]=1;
 62                     d[e.from]=d[x]+1;
 63                     Q.push(e.from);
 64                 }
 65             }
 66         }
 67     }
 68 
 69     int Augment(){
 70         int x=t,a=INF;
 71         while(x!=s){
 72             Edge &e=edges[p[x]];
 73             a=min(a,e.cap-e.flow);
 74             x=edges[p[x]].from;
 75         }
 76         x=t;
 77         while(x!=s){
 78             edges[p[x]].flow+=a;
 79             edges[p[x]^1].flow-=a;
 80             x=edges[p[x]].from;
 81         }
 82         return a;
 83     }
 84 
 85     int Maxflow(int s,int t){
 86         this->s=s;this->t=t;
 87         BFS();
 88         memset(num,0,sizeof(num));
 89         for(int i=0;i<n;i++)num[d[i]]++;
 90         int x=s;
 91         memset(cur,0,sizeof(cur));
 92         while(d[s]<n){
 93             if(x==t){
 94                 flow+=Augment();
 95                 x=s;
 96             }
 97             int ok=0;
 98             for(int i=cur[x];i<G[x].size();i++){
 99                 Edge &e=edges[G[x][i]];
100                 if(e.cap>e.flow&&d[x]==d[e.to]+1){
101                     ok=1;
102                     p[e.to]=G[x][i];
103                     cur[x]=i;
104                     x=e.to;
105                     break;
106                 }
107             }
108             if(!ok){
109                 int m=n-1;
110                 for(int i=0;i<G[x].size();i++){
111                     Edge &e=edges[G[x][i]];
112                     if(e.cap>e.flow)m=min(m,d[e.to]);
113                 }
114                 if(--num[d[x]]==0)break;
115                 num[d[x]=m+1]++;
116                 cur[x]=0;
117                 if(x!=s)x=edges[p[x]].from;
118             }
119         }
120         return flow;
121     }
122     void Print(int c){
123         int x=1,num=0;
124         memset(have,0,sizeof(have));
125         for(int i=1;i<=maxball&&num<c;i++){
126             if(!have[i]){
127                 printf("%d",i);
128                 x=i;
129                 num++;
130                 while(!have[x]){
131                     have[x]=true;
132                     for(int j=0;j<G[x].size();j++){
133                         Edge &e=edges[G[x][j]];
134                         if(e.flow==0||e.to==0)continue;
135                         x=e.to-maxball;
136                         printf(" %d",x);
137                     }
138                 }
139                 printf("\n");
140             }
141         }
142     }
143 }isap;
144 int main(){
145 //    freopen("e:\\in.txt","r",stdin);
146     isap.init(2*maxball+2);
147     for(int k=1;k<=100;k++){
148         for(int i=1;k*k-i>i;i++){
149             p.push_back(Pair(i,k*k-i));
150         }
151     }
152     sort(p.begin(),p.end(),Cmp);
153     scanf("%d",&n);
154     int mi=n;
155     if(n>=50)mi=1290;
156     for(int m=1;;m++){
157         isap.AddEdge(0,m,1);
158         isap.AddEdge(m+maxball,2*maxball+1,1);
159         while(idx<p.size()&&p[idx].y<=m){
160             Pair &po=p[idx];
161             isap.AddEdge(p[idx].x,p[idx].y+maxball,1);
162             idx++;
163         }
164         if(m>=mi&&m-isap.Maxflow(0,2*maxball+1)==n+1){
165             printf("%d\n",m-1);
166             isap.Print(n);break;
167         }
168     }
169     return 0;
170 }

 

转载于:https://www.cnblogs.com/7391-KID/p/7448118.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值