NOIP 模拟总结10.17

来源:互联网 发布:淘宝手机端店铺装修图 编辑:程序博客网 时间:2024/06/05 18:38

T1
低价购买
问题描述:
“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(216范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。
这里是某支股票的价格清单:
日期 1 2 3 4 5 6 7 8 9 10 11 12
价格 68 69 54 64 68 64 70 67 78 62 98 87
最优秀的投资者可以购买最多4次股票,可行方案中的一种是:
日期 2 5 6 10
价格 69 68 64 62

输入说明:
第1行: N (1 <= N <= 5000),股票发行天数
第2行: N个数,是每天的股票价格。(0<=价格<=100000)

输出说明:
输出文件仅一行包含两个数:最大购买次数和拥有最大购买次数的方案数(<=231)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

样例输入:
12
68 69 54 64 68 64 70 67 78 62 98 87

样例输出:
4 2

请注意题面描述最后一行
要注意判重
!!!
没判重的dxg 只得了40分

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>using namespace std;int n;int w[10005];int f[10005];int g[10005];int pre[10005];int wei[10005];inline int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}inline int dng(int l,int r,int val){    while(l<=r){        int mid=(l+r)/2;        if(f[mid]==val) return mid;        else if(f[mid]>=val) r=mid-1;        else l=mid+1;    }    return l;}int main(){    freopen("buylow.in","r",stdin);    freopen("buylow.out","w",stdout);    n=read();    for(register int i=1;i<=n;++i) {        int v;        v=read();        w[n-i+1]=v;        f[i]=1;    }    for(register int i=2;i<=n;i++){        for(register int j=1;j<i;j++){             if(w[j]<w[i]){                f[i]=max(f[i],f[j]+1);             }        }    }    for(register int i=1;i<=n;i++)      if(f[i]==1) g[i]=1;     for(register int i=2;i<=n;i++){        for(register int j=1;j<=i-1;j++){            if(w[i]==w[j]&&f[i]==f[j])               g[j]=0;            else if(w[i]>w[j]&&f[i]==f[j]+1){                g[i]+=g[j];            }        }    }    int ans=0;    for(int i=1;i<=n;i++){        ans=max(ans,f[i]);    }    printf("%d ",ans);    int ans1=0;    for(int i=1;i<=n;i++){        if(f[i]==ans){            ans1+=g[i];        }    }    printf("%d",ans1);    return 0;}

T2
通讯线路
问题描述:
某地区共有n座村庄,每座村庄的坐标用一对整数(x, y)表示,现在要在村庄之间建立通讯网络。通讯工具有两种,分别是需要铺设的普通线路和卫星设备。卫星设备数量有限,只能给k个村庄配备卫星设备。拥有卫星设备的村庄互相间直接通讯;铺设了线路的村庄之间也可以通讯。卫星分配是不受限制的。
问怎样合理的分配卫星和铺设线路,使得在保证每两座村庄之间都可以直接或间接地通讯的前提下,铺设线路的总长度最短。

输入说明:
第一行两个数:n,k(0≤k≤n≤2000)
接下来n行,每行两个整数(x,y)数描述一个村庄。(-10000≤x,y≤10000)

输出说明:
仅一行,代表总长度,精确到0.0001

样例输入:
20 8
137 824
761 14
68 151
194 758
149 138
314 90
809 404
964 877
471 66
177 546
73 977
397 560
928 653
199 486
736 44
985 801
621 509
444 140
88 508
556 327

样例输出:
1355.4195

这是我在考场上除了spfa想出的第一个正解
(我不会告诉你第一次数组开大了QAQ)
以下是考试代码~直接过掉

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <cmath>using namespace std;int n,k;double dis[2005][2005];int fa[20005];double bian[2005];int find(int x){    if(fa[x]!=x) fa[x]=find(fa[x]);    return fa[x];}inline int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}struct NODE{    int x,y;}zh[2005];inline double cacu(int x,int y,int xx,int yy){    double x1=fabs(double(x)-double(xx));    double x2=fabs(double(y)-double(yy));    return sqrt(x1*x1+x2*x2);}int tot=0;struct tree{    int l;    int r;    double v;}tr[2001005];inline void add(int x,int y,double z){    ++tot;    tr[tot].l=x;    tr[tot].r=y;    tr[tot].v=z;}inline bool cmp(tree a,tree b){    return a.v<b.v;}int main(){    freopen("line.in","r",stdin);    freopen("line.out","w",stdout);    n=read();    k=read();    for(register int i=1;i<=n;i++){        zh[i].x=read();        zh[i].y=read();    }    for(register int i=1;i<=n;i++) fa[i]=i;    for(register int i=1;i<=n;i++){        for(register int j=1;j<=n;j++){            if(i!=j)             dis[i][j]=dis[j][i]=cacu(zh[i].x,zh[i].y,zh[j].x,zh[j].y);            if(i==j) dis[i][j]=dis[j][i]=0.0;        }    }    for(register int i=1;i<=n;i++){        for(register int j=i+1;j<=n;j++){            add(i,j,dis[i][j]);        }    }    int anss=0;    double minn=0.0;    sort(tr+1,tr+tot+1,cmp);    for(register int i=1;i<=tot;i++){        int x=tr[i].l;        int y=tr[i].r;        int fx=find(x);        int fy=find(y);        if(fx!=fy){            ++anss;            fa[fx]=fy;            bian[anss]=tr[i].v;            minn+=bian[anss];        }        if(anss==n-1) break;    }    sort(bian+1,bian+anss+1);    for(register int i=anss;i>=n-k+1;i--)       minn-=bian[i];       printf("%.4lf",minn);    return 0;}

T3
墙壁粉刷
问题描述:
现在需要粉刷一列墙壁,墙壁被分成n段,为了节约用钱,科学家决定只粉刷其中一些段,同时为了美观,科学家要求每连续的m段墙壁中至少有两块被粉刷,现在已知粉刷每一段墙壁的费用。科学家要你帮他求出最少的费用。

输入说明:
第一行,n和m(2<=n <=10000,2<=m<=100)
第二行,n个非负整数,第i个数为粉刷第i段的费用。(费用<=10000)

输出说明:
一行,最小费用。

样例输入:
8 3
6 7 10 1 7 8 5 6

样例输出:
30
墙壁粉刷
分析:
这是一道动态规划题,我们可以设f[i][j]表示粉刷前i段,并且第i段被粉刷了,而且倒数第2段被粉刷的位置是i-j。那么我们可以得到转移方程为:
F[i][j]=min{f[i-j][k]}+a[i](其中a[i]表示第i个墙壁粉刷的费用,1<=k<=m-j)
这个方程状态量是n*m的,转移是m的,所以时间复杂度为O(n*m*m),因为常数非常小,所以这个动态规划是不会超时的。对于方程中k的选取,是根据每m段中有两端被粉刷而确定的。
最后,还需要注意的就是初始状态,我们需要假定在第0位和第-1位的墙壁已经被粉刷,这样就不需要特殊考虑边界条件了。

ps:这个还没改完,下面的是cbn的代码%%%

#include<cstdio>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<cstring>#include<iostream>using namespace std;int n,m;int a[10005];int f[10005][105];int ans=0x3f3f3f3f;int main(){  freopen("brush.in","r",stdin);  freopen("brush.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    memset(f,0x7f,sizeof f);    for(int i=2;i<=m;i++)        for(int j=1;j<i;j++)        f[i][i-j]=a[i]+a[j];    for(int i=m+1;i<=n;i++)        for(int j=i-m+1;j<=i+1;j++)    {        for(int k=i-m;k<j;k++)            f[i][i-j]=min(f[i][i-j],f[j][j-k]);        f[i][i-j]+=a[i];    }    for(int i=n-m+1;i<=n;i++)        for(int j=i+1;j<=n;j++)        ans=min(ans,f[j][j-i]);    printf("%d",ans);    fclose(stdin);    fclose(stdout);    return 0;}