链接: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(" "); 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 ",m-1); 166 isap.Print(n);break; 167 } 168 } 169 return 0; 170 }