8.20 附加赛3

本文深入解析了算法竞赛中的策略应用,包括贪心算法在特定条件下的最优解寻找,以及动态规划在复杂问题上的高效求解。通过实例分析,阐述了贪心算法在资源分配问题中的实践,和动态规划在树形结构问题上的应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


2018.8.20 附加赛3

比赛链接

A 斯(贪心)

题目链接

某状态下的最差情况收益就是min(wa,wb)-cost。当然是从大的取,同时尽量保证wa,wb均衡。
枚举一下,Ans时刻取个max就行了。
复杂度在于排序 O(nlogn)。Ai只有四位小数且<=10,可以用桶排降到O(n)。

//138ms 2392kb
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e5+5;

int n;
double A[N],B[N];
char IN[MAXIN],*SS=IN,*TT=IN;

inline double read()
{
    double x=0,y=0.1; register char c=gc();
    for(; !isdigit(c)&&c!='.'; c=gc());
    for(; isdigit(c); x=x*10+c-'0',c=gc());
    for(c=='.'&&(c=gc()); isdigit(c); x+=(c-'0')*y,y*=0.1,c=gc());
    return x;
}

int main()
{
    n=read();
    for(int i=1; i<=n; ++i) A[i]=read();
    for(int i=1; i<=n; ++i) B[i]=read();
    std::sort(A+1,A+1+n), std::sort(B+1,B+1+n);
    double Ans=0,nowa=0,nowb=0;
    for(int a=n,b=n,cost=0; a||b; ++cost,Ans=std::max(Ans,std::min(nowa,nowb)-cost))
    {
        if(!b||(nowa<=nowb && a)) nowa+=A[a--];
        else nowb+=B[b--];
    }
    printf("%.4lf\n",Ans);

    return 0;
}

B 给(DP)

题目链接

DP,f[i][j]表示当前有i个叶子,树中根到所有叶子的路径中向左的边都不超过j条,的方案数。
根节点的两棵左右子树算是两个独立的子问题,即枚举左子树的叶节点数k后,可以直接f[k]*f[i-k]拼起来。
再具体就是左子树中所有向左边不超过j-1条的叶节点都可以接上i,然后乘上右子树所有路径向左边不超过j条的方案数。
\(f[i][j]=\sum_{k=1}^{j-1}f[k][j-1]*f[i-k][j]\)
至于为什么只枚举左儿子而不用*2。。不知道。。求dalao解答。。(枚举是对称的?)
这样是O(n^3),得分35~40。
注意到转移是个卷积形式,用NTT转移 写的好的话能拿到60。

满分做法:还是要换种状态表示,考虑枚举一个一个放叶子:f[i][j]表示已有i个叶子,当前放的叶子到根节点的路径有j条向左边。
下一个点要么放在当前叶子的左儿子。这样不会改变叶子数,j要加1,即转移到f[i][j+1]。
要么放在最近有左儿子但是没放右儿子的点,作为其右儿子。这样能转移到叶子数+1,j-1的状态,即f[i+1][j-1]。
f[i][0]就表示已有i个叶子,且所有有左儿子的点都有右儿子了,即k=i时的答案。
因为是枚举了所有状态下的唯二两种转移。。嗯。。所以说(这么说)没问题吧。。
复杂度O(n^2)。

唉 感觉两个DP都好迷啊。。

//376ms 95800kb(莫名慢好多 明明都差不多)
#include <cstdio>
#define mod (998244353)
#define rg register
//#define Add(x,v) x+=v,x>=mod&&(x-=mod)
const int N=5005;

int f[N][N];
inline void Add(int &x,int v){
    x+=v, x>=mod&&(x-=mod);
}

int main()
{
    int m,n; scanf("%d%d",&m,&n);
    f[1][0]=1;
    for(rg int i=1; i<n; ++i)
    {
        Add(f[i][1],f[i][0]);
        for(rg int j=1; j<m; ++j)
            Add(f[i][j+1],f[i][j]), Add(f[i+1][j-1],f[i][j]);
    }
    for(rg int i=1; i<=n; ++i) printf("%d\n",f[i][0]);

    return 0;
}

C 普

题目链接

转载于:https://www.cnblogs.com/SovietPower/p/9524754.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值