zoukankan      html  css  js  c++  java
  • DAY2

    国庆集训Day2

    A. 小灯笼

    题目描述

    一条平直的公路上有(n)个小路灯,第(i)个路灯的坐标是(a_i)。小A需要把其中的(k)个点亮,使得每个小路灯与距离最近的被点亮的小路灯的距离的最大值最小。求这个最小值。

    输入格式

    (1)(2)个正整数(n,k)

    接下来(n)行,每行一个整数(a_i),表示第(i)个小路灯的坐标,保证(a_i)是严格单调递增的。

    输出格式

    (1)(1)个数表示答案。

    样例数据

    input

    6 3
    5
    6
    12
    19
    20
    27
    

    output

    6
    

    30分:

    不妨设(dp[i,j])表示前(i)个小路灯其中第(i)个亮,总共j个。
    (dp[i,j]=dp[l,j-1]+g(l+1,i))


    100分:二分

    一看题面,最小化最大值就知道这极有可能是二分。
    框架不难,按照贪心原则,当前灯能不亮就不亮。

    C++ AC代码

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int SIZE = 100000 + 5, INF = 1 << 30;
    int n, k;
    int a[SIZE];
    bool valid(int x)
    {
    	int last = -INF, cnt = 0;
    	for(int j = 1; j <= n; ++ j)
    	{
    		if(a[j] - last <= x) continue;
    		int i = j;
    		++ j;
    		while(j <= n)
    		{
    			if(a[j] - a[i] > x)
    			{
    				++ cnt;
    				last = a[j - 1];
    				-- j;	
    				break;
    			}
    			++ j;
    		}
    	}
    	if(a[n] - last > x) ++ cnt;
    	if(cnt <= k) return true;
    	return false;
    }
    int main()
    {
    	scanf("%d %d", &n, &k);
    	int L = INF, R = -1, mid;
    	for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
    	R = a[n] - a[1];
    	for(int i = 2; i <= n; ++ i) L = min(L, a[i] - a[i - 1]);
    	while(L < R)
    	{
    		mid = L + ((R - L) >> 1);
    		if(valid(mid)) R = mid;
    		else L = mid + 1;
    	}
    	printf("%d
    ", L);
    	return 0;
    }
    

    B. 序列

    题目描述

    设数列(P=[p_1,p_2,……,p_n]),定义(f(p,k)=[p_2,p_3,……,p_k,p_1,p_{k+2},p_{k+3},……,p_{2k},p_{k+1},……]),也就是把(P)(k)个分成一段(最后如果不足(k)个,把它们分到新的一段),然后将每段的首个元素移动到该段末尾。求(f(f(……f(f([1,2,……,n],2),3),……),n))

    输入格式

    (1)(1)个正整数(n)

    输出格式

    (1)(n)个数表示答案。

    样例数据

    input

    4
    

    output

    4 2 3 1
    

    此题实在是思维好题!
    首先,我们可以观察整个数组改变的位置。
    明显,我们可以把每次操作中不是首项的数不动,其他的数位置(+i)

    C++ AC代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int maxn = 1000000 + 5;
    int n, p[maxn << 1];
    int main()
    {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++ i) p[i] = i;
    	int delta = 0;
    	for(int i = 2; i <= n; ++ i)
    	{
    		int cur = 0;
    		for(int j = delta + 1; j <= n + delta; j += i) swap(p[j], cur);	
    		++ delta;
    		swap(p[n + delta], cur);
    	}
    	for(int i = n; i < n << 1; ++ i) printf("%d ", p[i]);
    	return 0;
    } 
    

    C. 路灯

    题目描述

    一条平直的公路上有(n)个路灯,第(i)个路灯的坐标是(a_i)。小A需要把其中的(k)个点亮,使得每个路灯与距离最近的被点亮的路灯的距离和最小。求这个最小值。

    输入格式

    (1)(2)个正整数(n,k)

    接下来(n)行,每行一个整数(a_i),表示第(i)个路灯的坐标,保证(a_i)是严格单调递增的。

    输出格式

    (1)(1)个数表示答案。

    样例数据

    input

    6 3
    5
    6
    12
    19
    20
    27
    

    output

    8
    

    100分 DP

    考虑(dp[i,j,k])代表([i,j])中点亮了(k)盏路灯的最小值。
    则:(dp[i,j,k]=min(dp[i,k,x]+dp[k+1,j,k-x]))
    初始化:(dp[i,j,1]=min(a[k]*(k-i)-s[k-1]+s[i-1]+s[j]-s[k]-(j-k)*a[k]))

    C++ AC代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int maxn = 200 + 5, maxk = 55; 
    int n, k, a[maxn], s[maxn] = {}, dp[maxn][maxn][maxk];
    int main()
    {
    	scanf("%d %d", &n, &k);
    	for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
    	for(int i = 1; i <= n; ++ i) s[i] = s[i - 1] + a[i];
    	memset(dp, 0x3f, sizeof(dp));
    	for(int i = 1; i <= n; ++ i)
    	{
    		for(int j = i; j <= n; ++ j)
    		{
    			for(int p = i; p <= j; ++ p)
    			{
    				dp[i][j][1] = min(dp[i][j][1], (p * 2 - i - j) * a[p] - s[p - 1] + s[i - 1] + s[j] - s[p]);
    			} 
    		}
    	}
    	for(int len = 2; len <= n; ++ len)
    	{
    		int i = 1, j = len;
    		for(; j <= n; ++ i, ++ j)
    		{
    			for(int l = 2; l <= k; ++ l)
    			{
    				for(int p = 1; p < l; ++ p)
    				{
    					for(int x = i; x <= j; ++ x) 
    					{
    						dp[i][j][l] = min(dp[i][j][l], dp[i][x][p] + dp[x + 1][j][l - p]);
    					}
    				} 
    			}
    		}
    	}
    	printf("%d
    ", dp[1][n][k]);
    	return 0;
    } 
    

    DP(正解)

    我们设(dp[i,j])选点亮(i)个灯共亮了(j)

    D. 匹配

    题目描述
    给定一颗(n)个点(n−1)条边的树,每条边的长度都是(1)(i)号节点的权是(a_i)。如果三个节点两两在树上的距离都是(4),那么称这三个节点构成了一个“组合”,一个“组合”的权是三个节点的权的乘积。求所有“组合”的权的和。

    输入格式

    (1)行一个整数(n)

    接下来(1)(n)个正整数,第(i)个数表示(a_i)

    接下来(n−1)行,每行(2)个正整数(u,v),表示(u)(v)间有一条边。保证输入的是一颗树。

    输出格式

    (1)(1)个数表示答案。

    样例数据

    input

    7
    1 2 3 4 5 6 7
    1 2
    2 3
    1 4
    4 5
    1 6
    6 7
    

    output

    105
    

    这道题实际上是树上的基本应用。
    我们很容易知道:一个“组合”成立,当且仅当这三个点(x,y,z)距离公共点为(2)

    DP暴力求解,时间复杂度为(O(n))

    C++ AC代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<cmath>
    using namespace std;
    const int SIZE = 100000 + 5;
    vector <int> G[SIZE];
    int n, a[SIZE];
    long long f[SIZE] = {}, fa[SIZE] = {}, ans = 0;
    void dfs(int u, int Fa)
    {
    	fa[u] = Fa;
    	for(int i = 0; i < G[u].size(); ++ i)
    	{
    		int v = G[u][i];		
    		if(v == Fa) continue;
    		f[u] += a[v]; 
    		dfs(v, u);
    	}
    	return;
    }
    void solve(int u)
    {
    	long long p1 = 0, p2 = 0, p3 = 0;
    	for(int i = 0; i < G[u].size(); ++ i)
    	{
    		int v = G[u][i];
    		if(v != fa[u]) 
    		{
    			solve(v); 
    			p3 += p2 * f[v];
    			p2 += p1 * f[v];
    			p1 += f[v];
    		}
    		else
    		{
    			long long val = f[v] - a[u] + a[fa[v]];
    			p3 += p2 * val;
    			p2 += p1 * val;
    			p1 += val;
    		}
    	}
    	ans += p3;
    	return;
    }
    int main()
    {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++ i) scanf("%lld", &a[i]);
    	for(int i = 1; i < n; ++ i)
    	{
    		int u, v;
    		scanf("%d %d", &u, &v);
    		G[u].push_back(v), G[v].push_back(u);
    	}
    	dfs(1, 0);
    	solve(1);
    	printf("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    关于拉格朗日乘子法和KKT条件
    深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
    安全模式下运行Windows installer并卸载程序
    win10 进入安全模式的方法
    支持向量机通俗导论(理解SVM的三层境界)
    Windows系统防火墙用法
    浏览器起始页被篡改恶意跳转解决方法
    网络熟知端口号
    SSL/TLS协议运行机制的概述
    分布式拒绝服务攻击 DDoS
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13762923.html
Copyright © 2011-2022 走看看