刷题记录NC16857 [NOI1999]生日蛋糕,NC16752 [NOIP2000]单词接龙,NC204418 新集合,NC20566 [SCOI2010]游戏

NC16857 [NOI1999]生日蛋糕

题目链接

关键点:

1、如何剪枝:

n为总体积,m为总层数

(1)先考虑每一层的r和h的上下界

r:因为下层的圆柱体会大于上层圆柱体,因此当前层的r至少要为当前层数,此为下界

上界:设当前已有的体积为v,那么pi*(n-v)>=pi*r*r*h;

得r<=(int)sqrt((n-v)/h) 转换成 r<=(int)sqrt(n-v);

r还有一个上界为底下一层的R,r<=R-1;

因此r<=min((int)sqrt(n-v), r[deep+1]-1);

同理的算法算h

h[deep] = min((int)(double)(n-v)/r[deep]/r[deep], h[deep+1]-1)+1

(2)当前的表面积+最小的可能表面积<=ans

(3)当前的体积+最小可能的体积<=n;

(4)当前的体积算出的表面积+当前已有的表面积<ans

(2*(目标体积-已有体积)/r + 已有表面积)>=ans)

完整代码

# include <bits/stdc++.h>
using namespace std;
const int inf = 10000000;
int n, m, ans=inf;
int mins[30];
int minv[30];
int r[30], h[30];
void dfs(int deep, int s, int v)
{
    if (deep==0)
    {
        if (v==n)
        ans = min(ans, s);
        return ;
    }
    r[deep] = min((int)sqrt(n-v), r[deep+1]-1);
    while (r[deep]>=deep)
    {
        h[deep] = min((int)(double)(n-v)/r[deep]/r[deep], h[deep+1]-1)+1;
        while (h[deep]>deep)
        {
            --h[deep];
            if (mins[deep]+s>ans) continue;
            if (minv[deep]+v>n) continue;
            if (2.0*(n-v)/r[deep]+s > ans) continue;
            if (deep == m) s = r[deep]*r[deep];
            dfs(deep-1, 2*r[deep]*h[deep]+s, v+r[deep]*r[deep]*h[deep]);
            if (deep == m) s=0;
        }
        --r[deep];
    }
}
int main()
{
    cin>>n>>m;
    for (int i=1; i<=m; i++)
    {
        mins[i] = mins[i-1]+2*i*i;
        minv[i] = minv[i-1]+i*i*i;
    }
    h[m+1] = r[m+1] = inf;
    dfs(m, 0, 0);
    if (ans == inf) cout<<"0"<<endl;
    else
        cout<<ans<<endl;
    
    return 0;
}

NC16752 [NOIP2000]单词接龙

题目链接

关键点:

1、如何判断两个字符串是否重合,重合部分为多少

采用从a串尾,对着b串头对应判断的方法,从a串末尾枚举a,b串从哪个位置开始重合

要特判a串长度为1,不然重合长度会为0

2、易错点:每个字符串可以使用两次

完整代码

# include <bits/stdc++.h>
using namespace std;
int n, ans;
string s[30];
string fir;
int vis[30];
int check(string a, string b)
{
    int len = min(a.length(), b.length());
    for (int i=1; a.length()==1? i<=len: i<len; i++)
    {
        bool flag = true;
        for (int j=0; j<i; j++)
        {
            if (a[a.size()-i+j] != b[j])
            {
                flag = false;
                break;
            }
        }
        if (flag == true)
            return i;
    }
    return 0;
}
void dfs(string st, int Now)
{
    ans = max(ans, Now);
    for (int i=1; i<=n; i++)
    {
        if (vis[i]>1) continue;
        int add = check(st, s[i]);
        if (add == 0) continue;
        vis[i]++;
        dfs(s[i], Now+s[i].length()-add);
        vis[i]--;
    }
}
int main()
{
    cin>>n;
    for (int i=1; i<=n; i++)
        cin>>s[i];
    cin>>fir;
    dfs(fir, 1);
    cout<<ans<<endl;
    
    
    return 0;
}

NC204418 新集合

题目链接

关键点:

1、对于每个数字有选或者不选,最后再判断有没有在给出的限制集合中出现的两个数

完整代码

/**
 * struct Point {
 *	int x;
 *	int y;
 *	Point(int xx, int yy) : x(xx), y(yy) {}
 * };
 */

class Solution {
public:
    int ans=0;
    int use[30];
    int solve(int n, int m, vector<Point>& limit) {
        memset(use, 0, sizeof(use));
        dfs(1, n, m, limit);
        return ans;
    }
    void dfs(int Now, int n, int m, vector<Point>& limit)
    {
        if (Now > n)
        {
            for (int i=0; i<m; i++)
            {
                int x = limit[i].x, y = limit[i].y;
                if (use[x] && use[y]) 
                    return ;
            }
            ans++;
            return ;
        }
        use[Now] = 1;
        dfs(Now+1, n, m, limit);
        use[Now] = 0;
        dfs(Now+1, n, m, limit);
    }
};

NC20566 [SCOI2010]游戏

题目链接

关键点:

dfs:

1、对于装备的两个属性,我们将其连成双向边,对于连成的树去判断该树是否有来回路,有就说明可以使用该树的所有结点,为一颗树说明该树只能用n-1个结点

2、如何dfs,先标记当前结点访问过了,然后遍历所有与当前结点连接的结点,看是否和上一个访问过的结点相同,相同说明重复就continue,再看是否访问过,访问过说明有重边。该步要一定要在上一个判断的后面,因为和上一个访问过的结点相同就是访问过,而此时不能说明有重边,该dfs还有对是否有重边返回一个判断

2、先将答案ans设为总结点数+1, 遍历所有结点,找未访问过的,且该结点所在的为一颗树,那么就更新ans,在dfs里计算该树的最大结点maxn,ans = min(ans, maxn)

最后输出ans-1,因为算出的最大结点,而结点所在为一棵树,说明只能攻击结点数-1

完整代码

# include <bits/stdc++.h>
using namespace std;
int n, maxn;
vector<int> ve[1000000+10];
int vis[1000000+10];
int dfs(int x, int fa)
{
    maxn = max(maxn, x);
    int flag = 0;
    vis[x] = 1;
    for (int i=0; i<ve[x].size(); i++)
    {
        int y = ve[x][i];
        if (y == fa) continue;;
        if (vis[y]) {flag = 1; continue;}
        if (dfs(y, x)) flag = 1;
    }
    return flag;
}
int main()
{
    cin>>n;
    for (int i=1; i<=n; i++)
    {
        int x, y;
        cin>>x>>y;
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    int ans = n+1;
    for (int i=1; i<=n; i++)
    {
        maxn = 0;
        if (!vis[i] && !dfs(i, 0))
            ans = min(ans, maxn);
    }
    cout<<ans-1<<endl;
    
    return 0;
}

并查集:

fa数组存父亲结点,初始化为本身值

maxn结点存该树的最大结点,初始化为本身值

b数组存该结点所在的树是否有回路,初始化为无回路0;

1、装备的属性看作为两点连边,然后在父亲所在的maxn中更新为,两个结点的最大值,父亲所在的b更新为,如果两人结点有一个的b为1,那么就为1,两个结点中有一个结点所在的树有回路,那么两个树连接就为有回路的树

2、ans =10000+1, 最后遍历所有属性值,遍历到fa[i]=i,父亲等于本身,说明信息都在i(父亲)中,那么看b[i]是否为0,为0,说明要更新ans值为, ans = min(maxn[i], ans);

3、最后输出ans-1,原因和dfs一致

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...
win11本地账户怎么改名?win11很多操作都变了样,用户如果想要...