牛客刷题-Day1

牛客刷题-Day1

今日题目:\(1001-1005\)

1003 可爱の星空

题目描述

“当你看向她时,有细碎星辰落入你的眼睛,真好。”——小可爱
在一个繁星闪烁的夜晚,卿念和清宇一起躺在郊外的草地上,仰望星空。
星语心愿,他们,想把这片星空的星星,连成一棵漂亮的树,将这美好的景色记录下来。
现在,天上共有 \(n\) 颗星星,编号分别为 \(1,2.....n\),一开始任何两个点之间都没有边连接。
之后,他们两个想在在 \((u,v)\) 之间连无向边,需要付出 \(|\)\(u\) 联通块大小-\(v\) 联通块大小\(|\) 的代价。
他们两个想用最少的代价来使这 \(n\) 个点联通,所以他们想知道最小代价是多少。

输入描述

第一行一个正整数,表示数据组数 \(T\)
接下来 \(T\) 行每行一个正整数,表示询问的 \(n\)

输出描述

\(T\) 行,每行一个数表示答案。

示例1

输入

1
5

输出

2

说明:\(1,2....5\) 五个点,连边顺序为 \((1,2),(3,4),(1,5),(5,3)\),代价为 \(0,0,1,1\),总代价为 \(2\),是 \(n=5\) 的时候最优答案。
虽然 \((1,2),(2,3),(3,4),(4,5)\) 也可以,但是代价为 \(0,1,2,3\),总代价为 \(6\),比 \(2\) 大。

备注

对于 \(20\%\) 的数据,\(T<=2,n<=10\)
对于 \(40\%\) 的数据,\(T<=10,n<=1000\)
对于 \(60\%\) 的数据,\(T<=100000,n<=100000\)
对于另外 \(40\%\) 的数据,\(T=1,n<=1000000000000\)

解题思路

分治:尽可能的平分,从单个点开始,两两联通。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int T;
LL n;

LL dfs(LL n) {
    if (n < 3)
        return 0;
    if (n & 1)
        return dfs(n / 2) + dfs(n / 2 + 1) + 1;
    return dfs(n / 2) * 2;
}

int main() {
    cin >> T;
    while (T--) {
        cin >> n;
        cout << dfs(n) << endl;
    }
    return 0;
}

1005 花店橱窗

题目描述

\(q\) 和他的老婆小 \(z\) 最近开了一家花店,他们准备把店里最好看的花都摆在橱窗里。
但是他们有很多花瓶,每个花瓶都具有各自的特点,因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果。
为了使橱窗里的花摆放的最合适,他们得想个办法安排每种花的摆放位置。
可是因为小 \(q\) 和小 \(z\) 每天都太忙,没有时间设计橱窗里花的摆法,所以他们想让你帮他们求出花摆放的最大美观程度和每种花所放的位置。
每种花都有一个标识,假设杜鹃花的标识数为 \(1\),秋海棠的标识数为 \(2\),康乃馨的标识数为 \(3\),所有的花束在放入花瓶时必须保持其标识数的顺序,即:
杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须放在康乃馨左边的花瓶中。
如果花瓶的数目大于花束的数目。则多余的花瓶必须空置,且每个花瓶中只能放一束花。
每种花放在不同的瓶子里会产生不同的美观程度,美观程度可能是正数也可能是负数。
上述例子中,花瓶与花束的不同搭配所具有的美观程度,如下表所示:

1 2 3 4 5
1 (杜鹃花) 1 2 3 4 5
2 (秋海棠) 5 21 -4 10 23
3 (康乃馨) -21 5 -4 -20 20

根据上表,杜鹃花放在花瓶 \(2\) 中,会显得非常好看;但若放在花瓶 \(4\) 中则显得十分难看。
为取得最大美观程度,你必须在保持花束顺序的前提下,使花束的摆放取得最大的美学值,并求出每种花应该摆放的花瓶的编号。

输入描述

\(1\) 行:两个整数 \(F\)\(V\),表示共有 \(F\) 种花,\(V\) 个花瓶。
\(2\) 行到第 \(F+1\) 行:每行有 \(V\) 个数,表示花摆放在不同花瓶里的美观程度值 \(value\)。(美观程度和小于 \(2^{31}\),美观程度有正有负)

输出描述

输出有两行:第一行为输出最大美观程度和的值,第二行有 \(F\) 个数表示每朵花应该摆放的花瓶的编号。
若有多种方案,输出字典序较小的方案(美观程度不变的情况下,花尽量往前放)。

示例1

输入

3 5 
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20

输出

53
2 4 5
备注

\(1≤𝐹≤𝑉≤100\)

解题思路

一开始笔者使用的是递归的方法,类似求解递升的排列,不出意外的超时了。

正确解法:动态规划。

  • 状态表示:\(f_{i,j}\) 表示摆放前 \(i\) 种花且放在前 \(j\) 个花瓶的方案,求解最大值;
  • 状态计算:考虑当前第 \(i\) 种花摆放在 \(j\) 花瓶中,那么第 \(i-1\) 种花可以摆放在 \(k\) 花瓶中,\(k\in [i-1,j],k\in Z\) 花瓶中,\(f_{i,j}=f_{i-1,k}+v_{i,j}\)

由于题目限制,第 \(i\) 种花必然摆放在第 \(i\) 或之后的花瓶中。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110;

int n, m, ans = -1e9;
int v[N][N], f[N][N]; // f[i][j] 表示摆到第 i 个花且位于花瓶 j
int pre[N][N], path[N];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> v[i][j];
    memset(f, 128, sizeof f);
    f[0][0] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= m - (n - i); j++)
            for (int k = i - 1; k < j; k++) {
                int t = f[i - 1][k] + v[i][j];
                if (t > f[i][j]) {
                    f[i][j] = t;
                    pre[i][j] = k;
                }
            }
    int ed = m;
    for (int i = n; i <= m; i++) {
        if (f[n][i] > ans) {
            ans = f[n][i];
            ed = i;
        }
    }
    for (int i = n; i >= 1; i--) {
        path[i] = ed;
        ed = pre[i][ed];
    }
    cout << ans << endl;
    for (int i = 1; i <= n; i++)
        cout << path[i] << ' ';
    return 0;
}
posted @ 2025-09-18 09:34  Cocoicobird  阅读(8)  评论(0)    收藏  举报