最小路径覆盖问题
Description
假设有
n
根柱子,现要按下述规则在这
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何
2
个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在
放
Input
输入文件第
1
行有
Output
程序运行结束时,将
文件的第一行是球数。
接下来的
n
行,每行是一根柱子上的球的编号。
Sample Input
4
Sample Output
11
1 8
2 7 9
3 6 10
4 5 11
Solution
方法一(网络流):
每根柱子互不干扰,可以把每根柱子看成一条路径——这不就成了最小路径覆盖问题了吗???
将相加为完全平方数的两个数连一条边,求解最小路径覆盖问题就可以解决
然后依次枚举 n0 ,直到所需柱子的个数大于 n ,即可得出答案(这里用枚举的原因是,每次不需要重新求解最大流)。
方法二(贪心):
还有一种贪心的方法。
现在如果有
易知,这样得出的答案为 f(n)={(n2−1)/2+n n≡1(mod 2)(n2−2)/2+n n≡0(mod 2) 。
下面,证明一下这个贪心的正确性。
若 n≡0(mod 2) ,假设 ans(n)>f(n) ,则 ans(n) 最小为 f(n)+1 。
此时,这 ans(n) 个数为 1、2、3、…(n2−1)/2+n+1 。
易知在前
n+1
个数中,最大的两个数之和为
(n2−1)/2+n+1+(n2−1)/2+n=n2+2n<(n+1)2
最小的两个数之和为
[(n2−1)/2+n+1−(n+1)+1]+[(n2−1)/2+n+1−(n+1)+2]=n2+2>n2
即这
n+1
个数任意两个数之和都夹在两个完全平方数之间,即任意两个数之和都不为完全平方数。
所以,这
n+1
个数都不能放在同一根柱子上,与只有
n
根柱子相矛盾。
同理,当
综上所述,这个贪心是正确的。
Code(只给出网络流版本)
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <queue>
- #define Min(x,y) ((x)<(y)?(x):(y))
- #define Max(x,y) ((x)>(y)?(x):(y))
- using namespace std;
- const int INF=0x3f3f3f3f;
- int m,n,s,t=100000-1,cnt,nx;
- int weight;
- int low[1000100],head[1000100],nxt[1000100],data[1000100];
- int dis[1000100];
- bool vis[1000100];
- bool he[1000010];
- queue<int>q;
- void add(int x,int y,int z){
- nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;
- nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;
- }
- bool BFS(){
- memset(dis,-1,sizeof dis);
- q.push(s);dis[s]=0;
- while(!q.empty()){
- int now=q.front();q.pop();
- for(int i=head[now];i!=-1;i=nxt[i])
- if(low[i]&&dis[data[i]]<0){dis[data[i]]=dis[now]+1;q.push(data[i]);}
- }
- return dis[t]>0;
- }
- int dfs(int now,int flow){
- if(now==t)return flow;
- int Flow;
- for(int i=head[now];i!=-1;i=nxt[i]){
- if(low[i]&&dis[data[i]]==dis[now]+1){
- if(Flow=dfs(data[i],Min(flow,low[i]))){
- low[i]-=Flow;
- low[i^1]+=Flow;
- return Flow;
- }
- }
- }
- return 0;
- }
- void dfs2(int now){
- printf(”%d ”,now);
- vis[now]=true;
- for(int i=head[now];i!=-1;i=nxt[i]){
- if(!vis[data[i]]&&data[i]!=s&&data[i]!=t&&data[i]>n&&!low[i])dfs2(data[i]-2000);
- }
- }
- int work(int front){
- weight++;
- n=front;
- // for(int i=0;i<cnt;i+=2){
- // low[i]=1;
- // low[i+1]=0;
- // }
- for(int i=1;i<front;i++){
- if(he[i+front])add(i,front+2000,1);
- }
- add(s,front,1);add(front+2000,t,1);
- while(BFS()){
- int flag;
- while(flag=dfs(s,INF)){
- weight-=flag;
- }
- }
- return weight;
- }
- void work2(int front){
- memset(head,-1,sizeof head);
- cnt=0;
- n=front;
- for(int i=1;i<=front;i++)
- for(int j=i+1;j<=front;j++)
- if(he[i+j])add(i,j+2000,1);
- for(int i=1;i<=front;i++){add(s,i,1);add(i+2000,t,1);}
- while(BFS()){
- int flag;
- while(flag=dfs(s,INF));
- }
- }
- int main(){
- freopen(”ball.in”,“r”,stdin);
- freopen(”ball.out”,“w”,stdout);
- memset(head,-1,sizeof head);
- int ans=0,ll;
- scanf(”%d”,&nx);
- for(int i=1;i<=100;i++)
- he[i*i]=true;
- for(ll=1;;ll++){
- int tmp=work(ll);
- bool flag=(tmp<=nx);
- if(!flag)break;
- }
- printf(”%d\n”,ll-1);
- work2(ll-1);
- for(int i=1;i<ll;i++)if(!vis[i]){
- dfs2(i);
- printf(”\n”);
- }
- return 0;
- }