2016.8.28测试解题报告(buylow,line,brush)

好吧,好久不见!

1.低价购买

问题描述:
“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(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

思路:
题目意思很明显,我们需要求一个序列的最长下降子序列,并且,我们需要求它的方案总数。首先,我们可以设f[i]表示以i结尾的最长下降子序列的长度为多少,那么:
F[0] = 0
F[i] = max{f[j]}+1 (j < i 且 a[j]>a[i])
这个方程的时间复杂度为O(n^2)。
为了记录方案数,我们可以设g[i]表示以i结尾的最长下降子序列的方案数为多少,那么:
g[0] = 0
g[i] = Ʃg[j]
但是题目中要求当方案看起来一样时,需要算作同一个方案,所以上面的所有j中a[j]相同的,我只要取j值最大的即可。
最后总时间复杂度为O(n^2)。

代码:

/*
2016.8.30 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int a[5005];
int n;
int f[5005];
int g[5005];
int ans,an;
int main()
{
    freopen("buylow.in","r",stdin);
    freopen("buylow.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    a[0]=2147483647;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=i-1;j++)
        if(a[i]<a[j]) f[i]=max(f[i],f[j]+1);
    g[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=i-1;j>=0;j--)
        {
            if(a[i]<a[j] && f[j]+1==f[i]) g[i]+=g[j];
            if(a[i]==a[j]) break;//结尾相同的我们只取一遍,既最大值
        }
    for(int i=1;i<=n;i++) if(f[i]>ans) ans=f[i];
    int tot=0;
    for(int i=1;i<=n;i++) if(f[i]==ans) tot+=g[i];
    cout<<ans<<" "<<tot<<endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.通讯线路

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

思路:
看到这道题就应该想起来最小生成树,不过如果这道题按照最小生成树的答案输出的话就会忽视卫星的存在。因为卫星可以连接k个联通分量既k棵树,所以我们只要先用Kruskal做一遍最小生成树,再删掉k-1条边权最大的边就可以了。具体实现的时候可以直接选择n-k条最小的边达到相同的目的。具体证明同Kruskal算法。

代码:

/*
2016.8.30 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
struct point
{
    int x,y;
}a[2005];
struct Edge
{
    int x,y;
    int val;
}eage[4004005];
int tot=0;
int cacl(point a,point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void add(int x,int y,int v)
{
    eage[++tot].x=x;
    eage[tot].y=y;
    eage[tot].val=v;
}
bool cmp(Edge a,Edge b) {return a.val<b.val;}
int f[2005];
int findx(int x)
{
    int r=x;
    while(f[r]!=r)
        r=f[r];
    return r;
}
int n,k;
int top=0;
int tim=0;
double ans=0;
int main()
{
    freopen("line.in","r",stdin);
    freopen("line.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        if(i!=j) add(i,j,cacl(a[i],a[j]));
    for(int i=1;i<=n;i++) f[i]=i;
    sort(eage+1,eage+1+tot,cmp);
    while(1)
    {
        top++;
        int g1=findx(eage[top].x);
        int g2=findx(eage[top].y);
        if(g1!=g2)
        {
            f[g2]=g1;
            tim++;
            ans+=sqrt(eage[top].val);
            if(tim==n-k) break;
        }
    }
    printf("%.4llf",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

3.粉刷墙壁

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

思路:
一眼dp。可以先假设第0块墙壁和第-1块墙壁已经被粉刷过了,所以说可以设状态为:满足第i块墙壁之前的所有墙壁段的要求,且粉刷的最后一块墙壁是第i块、粉刷的倒数第二块墙壁是第i-j块的方案的最低花费为f[i][j]。则有状态转移方程:f[i][j]=min(f[i-j][k])+a[i](既假设粉刷的最后一块墙壁是第i块,枚举所有可以满足条件的状态)

代码:

/*
2016.8.30BulaBulaCHN
*/
#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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值