zoukankan      html  css  js  c++  java
  • Daliy Algorithm (数学,构造,树)-- day 61

    Nothing to fear

    those times when you get up early and you work hard; those times when you stay up late and you work hard; those times when don’t feel like working — you’re too tired, you don’t want to push yourself — but you do it anyway. That is actually the dream. That’s the dream. It’s not the destination, it’s the journey. And if you guys can understand that, what you’ll see happen is that you won’t accomplish your dreams, your dreams won’t come true, something greater will. mamba out


    那些你早出晚归付出的刻苦努力,你不想训练,当你觉的太累了但还是要咬牙坚持的时候,那就是在追逐梦想,不要在意终点有什么,要享受路途的过程,或许你不能成就梦想,但一定会有更伟大的事情随之而来。 mamba out~

    2020.4.20


    Three Integers

    由题意可知 (A|B B|C)
    故 : (xA = B yb = C)
    (xyA = C)
    // 由于 a b c得范围都小于 10^4 枚举
    A x y 得值
    枚举有个坑 题目并没有限定输出得数据范围 所以要自己计算数据范围 ,由于我没有当时做所以感受不到 2333
    拿到题目还是不要着急写代码

    1. 观察数据范围 推测可能使用得方法以及时间复杂度
    2. 尝试暴力思路计算时间复杂度
    3. 如果时间复杂度过高进行优化,或者对问题进行一些等价变化看是否能推断出一些新的性质
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <string>
    #include <cmath>
    #define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    
    using namespace std;
    typedef long long ll;
    const int MAX = 0x7ffffff;
    int t;
    
    void slove()
    {
    	int a , b ,c;
    	int A , B ,C;
    	int ans = MAX;
    	cin >> a >> b >> c;
    	for(int i = 1;i <= 11000;i ++)
    	{
    		for(int j = i;j <= 11000;j += i)
    		{
    			for(int k = j ;k <= 11000;k += j)
    			{
    				int tmp = abs(a - i) + abs(b - j) + abs(c - k);
    				if(tmp < ans)
    				{
    					A = i;B = j;C = k;
    					ans = tmp;
    				}
    			}
    		}
    	}
    	cout << ans << endl;
    	cout << A << " " << B << " " << C << endl;
    }
    int main()
    {
    	SIS;
    	cin >> t;
    	while(t--)
    	{
    		slove();
    	}
    }
    

    Construct the Binary Tree

    题解
    该问题是一个简单的构造问题,对于给定得n我们可以得到d得上街和下界
    如果给定得d不属于这个区间内那么就输出NO,否则对于区间内得任何d都是成立得

    int ld = 0,rd = n * (n - 1) / 2;
    // 得到下边界 : 完全二叉树
    
    // 巧妙得利用位运算满二叉树得每层得个数和每层深度
    
    每次换层的时候就是 相 & 为 0 的时候!!
    for(int i = 1,cd = 0;i <= n ;++ i)
    {
    	if(!(i & (i - 1))) ++ cd;
    	ld += cd - 1;
    }
    // 判断给定得 d 是否在区间之中 
    if(!(ld <= d && d <= rd))
    {
    	cout << "NO" << endl;
    	return;
    }
    

    那么如何去构造该问题:
    首先将其看作是一条链 (n*(n-1) / 2) 那么上界就是它,然后我们尝试去缩小范围
    我们首先需要找到深度最小的叶子结点,然后试着向上移动

    int v = -1;
    for(int i = 0;i < n ;++ i)
    {
    	if(!bad[i] && cnt[i] == 0 && (v == -1 || dep[v] > dep[i]))
    	{
    		v = i;
    	}
    }
    assert(v != -1);
    

    找到v结点之后
    我们找到一个顶点 p 它的深度比 v 小 2 并且其子树分支 < 2 如果我们可以找到一个这样得结点 p 我们就让 v 作为 p 得孩子结点并成功进行一次消除

    		int p = -1;
    		for(int i = 0;i < n ;++ i)
    		{
    			if(cnt[i] < 2 && dep[i] < dep[v] - 1 && (p == -1 || dep[p] < dep[i]))
    			{
    				p = i;
    			}
    		}
    

    更新操作

    	assert(dep[v] - dep[p] == 2);
    	--cnt[par[v]];  // v 的父亲节点的儿子 --
    	--dep[v];       // v 的深度 -- 
    	++cnt[p];       // p 结点的儿子节点个数++
    	par[v] = p;     // v 的父亲指向 p
    	--cur;          // 消去一层中的一个 d
    

    如果我们不能找到这样一个结点 p 就认为顶点v有它所能拥有的最小可能的深度,我们在将来不应该考虑它 此时我们可以标记这个结点 并且继续进行后续下一轮操作

    if(p == -1)
    {
    	bad[v] = -1;
    	continue;
    }
    

    如果在某一时刻我们找不到任何不差的叶子节点 v,那么答案是“NO”。否则,答案是“YES”。

    完整代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <string>
    #include <cassert>
    #include <cmath>
    #define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    #define lowbit(x) (x & -x)
    using namespace std;
    typedef long long ll;
    const int N = 5005;
    const int MAX = 0x7ffffff;
    int t , n , d;
    int tr[N << 2];
    void slove()
    {
    	cin >> n >> d;
    	// 得到上边界 : 是一条链
    	int ld = 0,rd = n * (n - 1) / 2;
    	// 得到下边界 : 完全二叉树
    
    	// 巧妙得利用位运算满二叉树得每层得个数和每层深度
    	for(int i = 1,cd = 0;i <= n ;++ i)
    	{
    		if(!(i & (i - 1))) ++ cd;
    		ld += cd - 1;
    	}
    	// 判断给定得 d 是否在区间之中 
    	if(!(ld <= d && d <= rd))
    	{
    		cout << "NO" << endl;
    		return;
    	}
    
    	vector<int> par(n); // 记录父节点
    
    	iota(par.begin(), par.end() , -1);
    
    	vector<int> cnt(n,1); // 记录儿子个数
    	cnt[n-1] = 0;
    
    	vector<int> bad(n);   //记录每个结点是否已经达到最优
    
    	vector<int> dep(n);  // 记录每个结点的当前深度
    	iota(dep.begin(), dep.end() , 0);
    
    	int cur = rd;
    
    	while(cur > d)
    	{
    		// 寻找深度最小的叶子节点 v
    		int v = -1;
    		for(int i = 0;i < n ;++ i)
    		{
    			if(!bad[i] && cnt[i] == 0 && (v == -1 || dep[v] > dep[i]))
    			{
    				v = i;
    			}
    		}
    		assert(v != -1);
    
    		int p = -1;
    		for(int i = 0;i < n ;++ i)
    		{
    			if(cnt[i] < 2 && dep[i] < dep[v] - 1 && (p == -1 || dep[p] < dep[i]))
    			{
    				p = i;
    			}
    		}
    		if(p == -1)
    		{
    			bad[v] = -1;
    			continue;
    		}
    		
    		assert(dep[v] - dep[p] == 2);
    		--cnt[par[v]];  // v 的父亲节点的儿子 --
    		--dep[v];       // v 的深度 -- 
    		++cnt[p];       // p 结点的儿子节点个数++
    		par[v] = p;     // v 的父亲指向 p
    		--cur;          // 消去一层中的一个 d
    	}
    
    	cout << "YES" << endl;
    	for(int i = 1;i <n ;++i)cout << par[i] + 1 << " ";
    	cout << endl;
    }
    int main()
    {
    	SIS;
    	cin >> t;
    	while(t--)
    	{
    		slove();
    	}
    }
    

    Sorted Adjacent Differences

    一个图就能说明这个巧妙的方法了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <cassert>
    #include <string>
    #include <cmath>
    #define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    #define lowbit(x) (x & -x)
    using namespace std;
    typedef long long ll;
    const int MAX = 0x7ffffff;
    int t;
    
    void slove()
    {
    	int n;
    	cin >> n;
    	vector<int> a(n , 0);
    	for(int i = 0;i < n ;i ++)cin >> a[i];
    	sort(a.begin(), a.end());
    	int mid = n >> 1;
    	int i = mid - 1,j = mid + 1;
    	cout << a[mid] << " ";
    	while(i >= 0 || j < n)
    	{
    		if(i >= 0)cout << a[i--] << " ";
    		if(j < n)cout << a[j++] << " ";
    	}
    	cout << endl;
    }
    int main()
    {
    	SIS;
    	cin >> t;
    	while(t--)
    	{
    		slove();
    	}
    }
    

    Filling Diamonds

    这个问题的关键是,无论你在哪里放置垂直的钻石,所有其他的地方都是由水平的钻石铺满,如下图所示

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <cassert>
    #include <string>
    #include <cmath>
    #define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    #define lowbit(x) (x & -x)
    using namespace std;
    typedef long long ll;
    const int MAX = 0x7ffffff;
    int t;
    
    void slove()
    {
    	ll n ;
    	cin >> n;
    	cout << n << endl;
    }
    int main()
    {
    	SIS;
    	cin >> t;
    	while(t--)
    	{
    		slove();
    	}
    }
    
  • 相关阅读:
    111
    实验 12 综合练习二
    实验 11结构体
    作业 5 指针应用1
    实验 10 指针2
    实验9 指针1
    实验8 数组2
    实验7
    321
    实验9-2
  • 原文地址:https://www.cnblogs.com/wlw-x/p/12741358.html
Copyright © 2011-2022 走看看