20260518紫题训练

P4636 [SHOI2011] 直线拟合

题意

一个工厂,每个时刻可以选择增加 111 生产力或生产出当前生产力大小的产品

先给出 nnn 个订单,可以选择一些完成

第 iii 个订单要求在 tit_iti 时刻交付 gig_igi 个产品,同时会给出 mim_imi 的报酬

问最大能获得多少报酬

其中,n≤15,ti≤105,gi≤109,mi≤109n\leq15,t_i\leq10^5,g_i\leq10^9,m_i\leq10^9n≤15,ti≤105,gi≤109,mi≤109

题解

考虑先 2n2^n2n 枚举所有的完成订单情况,于是问题由最优求解转化为判断

先假设我们考虑到第 iii 个订单,枚举后面的订单 jjj

令 T=tj−ti,Need=∑k=i+1jgk,XT=t_j-t_i,Need=\sum_{k=i+1}^jg_k,XT=tj−ti,Need=∑k=i+1jgk,X 为 [ti+1,tj]\big[t_i+1,t_j\big][ti+1,tj] 中加了几次生产力,MMM 为当前的生产力,RRR 为当前暂时存着没有提供的产品数量

则显然有 (M+X)(T−X)+R>=Need(M+X)(T-X)+R>=Need(M+X)(T−X)+R>=Need

展开是一个二次函数的形式,容易求解出 XXX 的范围

但,这么多个 XXX 的范围内,只有 iii 和 i+1i+1i+1 的这个解集是 '实' 的,因为其余的并不能保证是全部加生产力在全部生产

于是我们取 XXX 为 iii 和 i+1i+1i+1 的解集中最大的一个,顺序模拟即可

时间复杂度 O(n2×2n)O(n^2\times2^n)O(n2×2n)

代码

cpp 复制代码
#include<bits/stdc++.h>
# define ll long long
# define Maxn 20
# define db double
using namespace std;
int n,s[Maxn],top;
ll sum,ans;
struct Node{
    ll t,g,m;
    bool operator < (const Node &x) const{return t<x.t;};
}a[Maxn];

ll Calc(ll A,ll B,ll C) {
    if(B*B-4ll*A*C<0) return -1;
    db X=((-B+sqrt(B*B-4ll*A*C))*1.0)/(2ll*A);
    return floor(X);
}
bool check() {
    ll M=1,R=0;
    for(int i=1;i<top;i++) {
        ll Mn=a[s[i+1]].t-a[s[i]].t,Need=0;
        for(int j=i+1;j<=top;j++) {
            Need+=a[s[j]].g;
            ll X=Calc(1,M-(a[s[j]].t-a[s[i]].t),Need-M*(a[s[j]].t-a[s[i]].t)-R);
            Mn=min(Mn,X);
        }
        // if(top==3) {printf("%d: %lld\n",i,Mn);}
        if(Mn<0) return 0;
        R=(M+Mn)*(a[s[i+1]].t-a[s[i]].t-Mn)+R-a[s[i+1]].g;
        M+=Mn;
    }
    return 1;
}

int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i].t,&a[i].g,&a[i].m);
    sort(a+1,a+n+1);
    for(int S=0;S<(1<<n);S++) {
        s[++top]=0;
        for(int i=0;i<n;i++)
            if((S>>i)&1) s[++top]=i+1,sum+=a[i+1].m;
        if(check()) ans=max(ans,sum);
        top=sum=0;
    }
    printf("%lld\n",ans);
    return 0;
}

P3828 [SHOI2012] 火柴游戏

题意

P3828 [SHOI2012] 火柴游戏

题目背景

SHOI2012D1T1

题目描述

小明非常喜欢玩火柴游戏:首先用火柴棒摆出一个可能是错误的等式,然后通过添加、删除或移动火柴棒,使得等式成立。下图展示每个数字的样子:

我们只考虑形如"A = B"的式子,其中 A 和 B 是两个具有相同位数的整数。

小明可进行三种操作:

  1. 在任意位置添加一根火柴棒;

  2. 从任意位置删除一根火柴棒;

  3. 将任意一根火柴棒移动到另一个位置。

在完成所有操作后,等号两侧必须都是合法的数字,且完全相等。我们约定:

  1. 小明不能消除任何数字,也就是说,可以删除一个数字的部分火柴,但不能令它消失;

  2. 小明不能增加任何数字,也就是说,可以在一个已有的数字上添加火柴,或将火柴移动到一个已有的数字上,但不能凭空增加一个数字;

  3. 小明不能拆分或者合并数字,比如将一个 8 变成两个 1,或者将两个 1合并成一个 8;

  4. 其中代表 1 的火柴棒必须靠右边摆放,放在左边不是有效的数字。每种操作都有一定的代价:

 对一个添加操作,如果这是第iii次进行添加操作,这一步的费用为 p1×i+q1p_1\times i+q_1p1×i+q1

 对一个删除操作,如果这是第iii次进行删除操作,这一步的费用为p2×i+q2p_2\times i+q_2p2×i+q2

 对一个移动操作,如果这是第iii次进行移动操作,这一步的费用为p3×i+q3p_3\times i+q_3p3×i+q3

例如,小明在游戏中添加了 3 根火柴,移动了 1 根火柴,删除了 2 根火柴,那么他总的花费为[(p1×1+q1)+(p1×2+q1)+(p1×3+q1)]+(p3×1+q3)+[(p2×1+q2)+(p2×2+q2)][(p_1\times 1+q_1)+(p_1\times 2+q_1)+(p_1\times 3+q_1)]+(p_3\times 1+q_3)+[(p_2\times 1+q_2 )+(p_2\times 2+q_2)][(p1×1+q1)+(p1×2+q1)+(p1×3+q1)]+(p3×1+q3)+[(p2×1+q2)+(p2×2+q2)]。

小明想知道,他如何才能用最少的花费使等式成立。你能写个程序帮助他吗?

对于 100%数据,有L≤200L\le 200L≤200。

赛时思路 以及 题解

假如知道目标状态,那么就可以算出要多加几笔和删去几笔

分别记为 cnt1,cnt2cnt1,cnt2cnt1,cnt2

但由于有移动操作,所以还要枚举 cntcntcnt 为移动几次

那么如果记 gi,jg_{i,j}gi,j 为加 iii 笔,删 jjj 笔的最小代价,则有

gi,j=mink=0min(i,j)Cost1(i−k)+Cost2(j−k)+Cost3(k)g_{i,j}=min_{k=0}^{min(i,j)} Cost_1(i-k)+Cost_2(j-k)+Cost_3(k)gi,j=mink=0min(i,j)Cost1(i−k)+Cost2(j−k)+Cost3(k)

其中 Cost1/2/3iCost_{1/2/3} iCost1/2/3i 表示 增/删/移动增/删/移动增/删/移动 iii 笔的操作代价


但现在并不知道目标状态

考虑用一个 DPDPDP

设 fi,j,kf_{i,j,k}fi,j,k 为考虑前 iii 位,增 jjj 笔,删 kkk 笔能否实现

于是直接 DPDPDP 即可,时间复杂度 O(L3)O(L^3)O(L3)

代码

cpp 复制代码
#include<bits/stdc++.h>
# define Maxn 205
# define ll long long
using namespace std;
bool st[15][15]={{0,1,1,1,0,1,1,1},{0,0,0,0,0,0,1,1},{0,1,0,1,1,1,1,0},{0,1,0,0,1,1,1,1},{0,0,1,0,1,0,1,1},{0,1,1,0,1,1,0,1},{0,1,1,1,1,1,0,1},{0,1,0,0,0,0,1,1},{0,1,1,1,1,1,1,1},{0,1,1,0,1,1,1,1}};
int n;
ll p1,q1,p2,q2,p3,q3,g[Maxn*10][Maxn*10],ans=1e18;
char s[Maxn],t[Maxn];
int cnt1[15][15];
int cnt2[15][15];
bitset<Maxn*10> f[Maxn][Maxn*10];
void Init() {
    for(int i=0;i<=9;i++) {
        for(int j=0;j<=9;j++) {
            for(int k=1;k<=7;k++) {
                if(st[i][k]&&(!st[j][k])) cnt2[i][j]++;
                if((!st[i][k])&&st[j][k]) cnt1[i][j]++;
            }
        }
    }
}
int main() {
    scanf("%d%s%s",&n,s+1,t+1);
    scanf("%lld%lld%lld%lld%lld%lld",&p1,&q1,&p2,&q2,&p3,&q3),Init();
    f[0][0].set(0);
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=min(505,i*7);j++) {
            int cnts1,cnts2,cntt1,cntt2;
            for(int k=0;k<=9;k++) {
                cnts1=cnt1[s[i]-'0'][k],cnts2=cnt2[s[i]-'0'][k];
                cntt1=cnt1[t[i]-'0'][k],cntt2=cnt2[t[i]-'0'][k];
                if(j>=cnts1+cntt1)
                f[i][j]|=(f[i-1][j-cnts1-cntt1]<<(cnts2+cntt2));
            }
        }
    }
    for(int i=0;i<=min(505,n*7);i++) {
        for(int j=0;j<=n*7;j++) {
            g[i][j]=1e18;
            for(int k=0;k<=min(i,j);k++)
            g[i][j]=min(g[i][j],(1ll*(i-k)*q1+1ll*(i-k)*(i-k+1)/2*p1)+
                                 1ll*k*q3+1ll*k*(k+1)/2*p3+
                                 1ll*(j-k)*q2+1ll*(j-k)*(j-k+1)/2*p2);
        }
    }
    for(int i=0;i<=min(505,n*7);i++) {
        for(int j=0;j<=n*7;j++)
            if(f[n][i][j]) ans=min(ans,g[i][j]);
    }
        
    printf("%lld\n",ans);
    return 0;
}

P4636 [SHOI2011] 直线拟合

题意

给定 nnn 个点,定义一条直线 lll 的权 D(l)=maxi=1ndis(l,i)D(l)=max_{i=1}^ndis(l,i)D(l)=maxi=1ndis(l,i)

其中 dis(l,i)dis(l,i)dis(l,i) 为第 iii 个点到直线 lll 的距离

问权最小的直线的权为多少

其中,n≤105n\leq10^5n≤105

赛时思路

考虑将所有点旋转 θ\thetaθ ,此时只用考虑竖坐标最大的两个点,ansansans 为这两个点的竖坐标的平均值

推导得,竖坐标为 x2+y2×(sinαcosθ+cosαsinθ)\sqrt{x^2+y^2}\times(sin\alpha cos\theta+cos\alpha sin\theta)x2+y2 ×(sinαcosθ+cosαsinθ)

将 sin⁡α,cosα\sin\alpha,cos\alphasinα,cosα 替换成 x,yx,yx,y得

(x2+y2)×(cosθy+sinθx)(x^2+y^2)\times(\frac{cos\theta}{y}+\frac{sin\theta}{x})(x2+y2)×(ycosθ+xsinθ)

题解

考虑到这道题相当于用两条平行直线把整个点集包含在两条直线中间,并使直线间距离最小

而这就是旋转卡壳

代码

cpp 复制代码
#include<bits/stdc++.h>
# define Maxn 100005
# define db double
# define ll long long
# define pr pair<int,int>
# define fir first
# define sec second
using namespace std;
int T,n,x,y;
int s[Maxn],top;
int S[Maxn],Top;
db ans=1e18;
struct Node{
    int x,y;
    bool operator < (const Node &c) const{
        if(x==c.x) return y<c.y;
        return x<c.x;
    };
    bool operator == (const Node &c) const{return (x==c.x)&&(y==c.y);};
}a[Maxn];   
ll Cross(pr x,pr y) {return 1ll*x.fir*y.sec-1ll*x.sec*y.fir;}
ll Calc(int x,int y,int z) {return Cross({a[y].x-a[x].x,a[y].y-a[x].y},{a[z].x-a[y].x,a[z].y-a[y].y});}
ll qdis1(int x,int y,int z) {return abs(Cross({a[y].x-a[x].x,a[y].y-a[x].y},{a[z].x-a[x].x,a[z].y-a[x].y}));}
ll qdis2(int x,int y) {return 1ll*(a[y].x-a[x].x)*(a[y].x-a[x].x)+1ll*(a[y].y-a[x].y)*(a[y].y-a[x].y);}
int main() {T=1;
    while(T--) {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
        sort(a+1,a+n+1),n=unique(a+1,a+n+1)-a-1;
        for(int i=1;i<=n;i++) {
            while(top>=2&&Calc(s[top-1],s[top],i)>=0) top--;
            s[++top]=i;
        }
        for(int i=1;i<=top;i++) S[Top++]=s[i];

        top=0;
        for(int i=n;i>=1;i--) {
            while(top>=2&&Calc(i,s[top],s[top-1])<=0) top--;
            s[++top]=i;
        }
        for(int i=2;i<top;i++) S[Top++]=s[i];
        
        int p=1;
        for(int i=0;i<Top;i++) {
            int j=(i+1)%Top;
            while(qdis1(S[i],S[j],S[p])<qdis1(S[i],S[j],S[(p+1)%Top])) p=(p+1)%Top;
            ans=min(ans,(qdis1(S[i],S[j],S[p])*1.0)/sqrt(qdis2(S[i],S[j])));
        }
        // printf("S ");for(int i=0;i<Top;i++) printf("%d ",S[i]);printf("\n");
        printf("%.2lf\n",(ans*1.0)/2);
        top=Top=0,ans=1e18;
    }
    return 0;
}
相关推荐
玛卡巴卡ldf3 小时前
【LeetCode 手撕算法】(多维动态规划)不同路径、最小路径和、最长回文子串、最长公共子序列、编辑距离
java·数据结构·算法·leetcode·动态规划·力扣
被AI抢饭碗的人3 小时前
算法:数据结构
数据结构·算法
_深海凉_3 小时前
LeetCode热题100-验证二叉搜索树
算法·leetcode·职场和发展
shehuiyuelaiyuehao3 小时前
算法27,二维前缀和
开发语言·python·算法
蒟蒻的贤4 小时前
编译原理里的冲突到底是什么?
考研·算法
lingzhilab4 小时前
零知派ESP32——BLE Mesh蓝牙组网智能灯控系统(PIR感应+W2812三档调色)
c++·mfc
_深海凉_4 小时前
LeetCode热题100-二叉树的右视图
算法·leetcode·职场和发展
计算机安禾4 小时前
【c++面向对象编程】第29篇:定位new(placement new):在指定内存上构造对象
开发语言·c++·算法
淞綰4 小时前
c语言的练习-字符串的练习-寻找最长连续字符以及出现次数
c语言·数据结构·学习·算法·c语言的练习