zoukankan      html  css  js  c++  java
  • cogs_396_魔术球问题_(最小路径覆盖+二分图匹配,网络流24题#4)

    描述


    http://cojs.tk/cogs/problem/problem.php?pid=396

    连续从1开始编号的球,按照顺寻一个个放在n个柱子上,(i)放在(j)上面的必要条件是(i+j)是一个完全平方数.问做多能放到几号球.

    分析


    cogs是简化版,我在网上找了个完整版的测试数据,要求输出方案...

    求最大放几号球不方便,我们考虑枚举最大的球号,计算最少需要多少柱子.

    我们对于满足(j<i)且(i+j)是一个完全平方数的(i,j),从(j)向(i)连一条边.那么我们要用几条路径,将全部点都连成几串.这个路径就相当于是柱子啦.所以这就是一个求最小路径覆盖的问题.

    注意!!!是最小路径覆盖!不是最小边覆盖!!!

    我们把每个点(i)拆成(i)和(i'),建立二分图,如果(i,j)满足条件,就从(j)向(i')连一条边.

    对于一个路径覆盖中的一条路径,除了路径末端的那个点,也就是一个柱子最下面的那个球,其他点都有一个后继,在二分图中我们让这些点和它们的后继匹配.

    这样的话,由于路径数=路径末端点的个数=点的总数-非末端点的个数=点的总数-二分图中的匹配数,所以我们做一下二分图匹配就可以了.

    由于随着球号的增大,最少需要的柱子数量是单调不降的,所以可以二分.但是二分的话每次都要重新建图跑最大流,如果顺次枚举的话,每次只需要加一些边,然后在原来的残量网络中继续跑就OK了.

    至于输出方案,最后跑一遍最大球号对应的最大流,然后在二分图的右侧找容量是1的边(从左边流过来1的流量,那么右边回去的容量就会+1),把每个球上面的球号记下来,然后输出的时候从1开始,只要当前这个球还没有输出,就从这个球开始向上一直输出.

    注意:

    1.虽然答案最大是1600,但是完全平方数可能不止这么大,例如:(1000+1500=2500=500^2).

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn=1600+5,opposite=1600,maxm=1000000+5,INF=0x3fffffff;
     5 int n,cnt=1,s,t,max_flow,ans;
     6 int head[maxn<<1],q[maxn<<1],lv[maxn<<1],itr[maxn<<1],match[maxn];
     7 bool issquare[maxn<<1],vis[maxn];
     8 struct edge{
     9     int to,cap,next;
    10     edge(){}
    11     edge(int to,int cap,int next):to(to),cap(cap),next(next){}
    12 }g[maxm];
    13 void add_edge(int u,int v,int cap){
    14     g[++cnt]=edge(v,cap,head[u]); head[u]=cnt;
    15     g[++cnt]=edge(u,0,head[v]); head[v]=cnt;
    16 }
    17 void bfs(){
    18     int front=0,tail=1;
    19     q[front]=s;
    20     memset(lv,-1,sizeof lv); lv[s]=0;
    21     while(front<tail){
    22         int u=q[front++];
    23         for(int i=head[u];i;i=g[i].next){
    24             int v=g[i].to;
    25             if(lv[v]<0&&g[i].cap>0){
    26                 lv[v]=lv[u]+1;
    27                 q[tail++]=v;
    28             }
    29         }
    30     }
    31 }
    32 int dfs(int u,int f){
    33     if(u==t) return f;
    34     for(int &i=itr[u];i;i=g[i].next){
    35         int v=g[i].to;
    36         if(g[i].cap>0&&lv[u]<lv[v]){
    37             int d=dfs(v,min(g[i].cap,f));
    38             if(d>0){
    39                 g[i].cap-=d;
    40                 g[i^1].cap+=d;
    41                 return d;
    42             }
    43         }
    44     }
    45     return 0;
    46 }
    47 void Dinic(){
    48     int flow=0,f;
    49     for(bfs();lv[t]>0;bfs()){
    50         for(int i=s;i<=t;i++) itr[i]=head[i];
    51         while((f=dfs(s,INF))>0) flow+=f;
    52     }
    53     max_flow+=flow;
    54 }
    55 void solve(){
    56     max_flow=0;
    57     for(int i=1;;i++){
    58         ans=i-1;
    59         add_edge(s,i,1);
    60         add_edge(i+opposite,t,1);
    61         for(int j=1;j<i;j++)if(issquare[i+j]) add_edge(j,i+opposite,1);
    62         Dinic();
    63         if(i-max_flow>n) break;
    64     }
    65     memset(head,0,sizeof head); cnt=1; max_flow=0;
    66     for(int i=1;i<=ans;i++){
    67         add_edge(s,i,1);
    68         add_edge(i+opposite,t,1);
    69         for(int j=1;j<i;j++)if(issquare[i+j]) add_edge(j,i+opposite,1);
    70     }
    71     Dinic();
    72 }
    73 void print(){
    74     int p;
    75     printf("%d
    ",ans);
    76     for(int i=opposite+1;i<=ans+opposite;i++)for(int j=head[i];j;j=g[j].next)if(g[j].cap==1){
    77         match[g[j].to]=i-opposite;
    78         break;
    79     }
    80     for(int i=1;i<=ans;i++)if(!vis[i]){
    81         p=0;
    82         for(int j=i;j;j=match[j]){
    83             printf("%d ",j);
    84             vis[j]=true;
    85         }
    86         printf("
    ");
    87     }
    88 }
    89 void init(){
    90     scanf("%d",&n);
    91     s=0; t=3200+1;
    92     for(int i=1;i*i<=3200;i++) issquare[i*i]=true;
    93 }
    94 int main(){
    95     init();
    96     solve();
    97     print();
    98     return 0;
    99 }
    View Code

    算法实现题 8-4 魔术球问题(习题 8-14)
    «问题描述:
    假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,1⁄4的球。
    (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

  • 相关阅读:
    (补充)10.Hibernate框架的查询方式
    12.Hibernate多对多关系
    11.Hibernate一对多关系
    (补充)06.Hibernate持久化类&持久化对象
    09.Hibernate中的事务与并发
    08.Hibernate的一级缓存-->>Session
    07.Hibernate常用的接口和类---Session接口☆☆☆☆☆
    05.Hibernate常用的接口和类---Configuration类和作用
    04.Hibernate常用的接口和类---SessionFactory类和作用
    python学习笔记(字典)
  • 原文地址:https://www.cnblogs.com/Sunnie69/p/5576240.html
Copyright © 2011-2022 走看看