zoukankan      html  css  js  c++  java
  • 【网络流】魔术球问题

    魔术球问题

    题目链接

    题目描述

    «问题描述:

    假设有(n)((4leq n leq 55))根柱子,现要按下述规则在这(n)根柱子中依次放入编号为(1,2,3,...)的球。

    ((1))每次只能在某根柱子的最上面放球。

    ((2))在同一根柱子中,任何(2)个相邻球的编号之和为完全平方数。

    试设计一个算法,计算出在(n)根柱子上最多能放多少个球。例如,在(4)根柱子上最多可放(11)个球。

    «编程任务:

    对于给定的(n),计算在(n)根柱子上最多能放多少个球。

    输入输出格式

    输入格式

    (1)行有(1)个正整数(n),表示柱子数。

    输出格式

    程序运行结束时,将(n)根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的(n)行,每行是一根柱子上的球的编号。

    输入输出样例

    输入样例

    4
    

    输出样例

    11
    1 8
    2 7 9
    3 6 10
    4 5 11
    

    (Solution)

    第一眼没思路,第二眼还是没有。

    没思路想耍流氓二分。似乎可以,显然柱子越多,放的球越多(至少不会更少),满足单调性。那就二分答案判断呗。

    怎么判断呢?

    鉴于这是一道网络流的题。。。

    图怎么建?什么情况下,两个点之间能有连边?满足两点标号之和为完全平方数。数范围不大,可以(n^2)枚举。但仅仅这样似乎不行。因为操作对象是点,不是边,因此要将点拆开。拆开之后,一边链接起点,拆出来的点链接终点。至于点与点之间,则是小的链接大的。边权都是(1)

    然后就是最大流板子。找到答案之后,再处理每条边上的点就好了。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    #define INF 10000000
    using namespace std;
    long long read(){
        long long x = 0; int f = 0; char c = getchar();
        while(c < '0' || c > '9') f |= c == '-', c = getchar();
        while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f? -x:x;
    }
    
    int n, s, t;
    
    struct szh{
        int to, next, w;
        inline void clear(){to = next = w = 0;}
        //因为要建很多遍图,所以要清空
    }a[400000];
    int cnt = 1, hd[10000];
    inline void add(int u, int v, int w){//连边
        a[++cnt].to = v, a[cnt].w = w, a[cnt].next = hd[u], hd[u] = cnt;
        a[++cnt].to = u, a[cnt].w = 0, a[cnt].next = hd[v], hd[v] = cnt;
    }
    
    int Q[10000], dis[10000];
    queue<int> q;
    bool bfs(){//求增广路
        memset(dis, 0, sizeof dis);
    	dis[s] = 1, q.push(s);
        while(!q.empty()){
            int u = q.front(); q.pop(); 
            for(int i = hd[u], v; v = a[i].to, i; i = a[i].next)
                if(a[i].w > 0 && !dis[v]) dis[v] = dis[u] + 1, q.push(v);
        }
        return dis[t];
    }
    int dfs(int u, int f){//增广
        if(u == t || !f) return f;
        int ans = 0;
        for(int i = hd[u], v;v = a[i].to, i; i = a[i].next)
            if(a[i].w > 0 && dis[v] == dis[u] + 1){
                int x = dfs(v, min(f, a[i].w));
                a[i].w -= x; a[i ^ 1].w += x, ans += x;
    			if(!(f -= x)) break;
            }
        return ans;
    }
    
    int mf;
    void dinic(){//板子
    	mf = 0;
        while(bfs()) mf += dfs(s, INF);
    }
    
    bool check(int x){
        for(int i = 0; i < 100000; ++i) a[i].clear();//初始化
    	memset(hd, 0, sizeof hd);
        cnt = 0, s = 0, t = 2 * x + 1;
        for(int i = 1; i <= x; ++i){//建图
            add(s, i, 1); add(i + x, t, 1);
            for(int j = i + 1; j <= x; ++j)//连边
                if(i + j == (int) sqrt(i + j) * sqrt(i + j)) add(i, j + x, 1);
        }
        dinic();
        return x - mf <= n;
    }
    
    int nxt[10000];
    bool use[10000];
    int main(){
        n = read();
        int l = 1, r = 2000, mid, ans = 0;
        while(l <= r){//二分
            mid = (l + r) >> 1;
            if(check(mid)) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        printf("%d
    ", ans);
        check(ans);
        for(int i = 1; i <= ans; ++i){
        	for(int j = hd[i], v; v = a[j].to, j; j = a[j].next){
        		if(v == s) continue;
        		if(!a[j].w) nxt[i] = v - ans;//记录路径
    		}
    		for(int j = hd[i + ans], v; v = a[j].to, j; j = a[j].next){
    			if(v != t) continue;
    			if(!a[j].w) use[i] = 1;//排除非起点
    		}
    	}
    	for(int i = 1; i <= ans; ++i){
    		if(use[i]) continue;
    		int u = i;
    		while(u){//从编号最小的点开始输出
    			printf("%d ", u); u = nxt[u];
    		}
    		printf("
    ");
    	}
        return 0;
    }
    
  • 相关阅读:
    常用软件
    树和二叉树的一些基本术语
    二分查找(Binary Search)
    GPIO模拟IIC接口信号质量分析
    PAT (Basic Level) Practise:1021. 个位数统计
    PAT (Basic Level) Practise:1017. A除以B
    PAT (Basic Level) Practise:1027. 打印沙漏
    文件操作:输出文件二进制数据
    阈值与平滑处理
    图像基本操作
  • 原文地址:https://www.cnblogs.com/kylinbalck/p/10603980.html
Copyright © 2011-2022 走看看