群赛 round#4 解题报告 (bam,three,tweak)

群赛 round#4 解题报告

赛制: OI 难度: noip+
得分: bam 100/100
three 100/100
tweak 0/100

T1 窃贼和火柴(bam)

【问题描述】
一个窃贼进入了火柴仓库,想要偷尽可能多的火柴。仓库里有 m 个集装箱,第 i 个集装箱里有 a i 个火柴盒,每个火柴盒里有 b i 根火柴。所有火柴盒大小相同。窃贼的帆布背包恰能容纳 n 个火柴盒。你的任务是找出窃贼能拿走的火柴的最大数量。他没时间重新调整火柴盒中的火柴,这就是他只是挑选不超过 n 个其包含火柴数之和最大的火柴盒的原因。
【输入文件】
输入文件 bam.in 第一行包含整数 n(1≤n≤2·10 8 )和整数 m(1≤m≤20)。
第 i+1 行包含一对整数 a i 和 b i ( 1≤a i ≤10 8 ,1≤b i ≤10)。所有输入的数字都是整数。
【输出文件】
输出文件 bam.out 包含唯一一个整数代表问题的答案。
【输入样例 1】
7 3
5 10
2 5
3 6
【输出样例 1】
62
【输入样例 2】
3 3
1 3
2 2
3 1
【输出样例 2】
7
这个题是比较简单的一道题.由于窃贼只能装n个火柴盒,所以我们利用贪心的思想,将所有火柴盒按火柴数从大到小排序,取前n个即可.写代码时认真一点,应该不会出错.
代码如下:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#define ll long long
using namespace std;
int n,m;
struct nod{
    int a,b;
}c[20];
ll ans;
int cmp(nod x,nod y)
{
    return ((x.b>y.b)||(x.b==y.b && x.a>y.a));
}
int main()
{
    freopen("bam.in","r",stdin);
    freopen("bam.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++) scanf("%d%d",&c[i].a,&c[i].b);
    sort(c,c+m,cmp);
    for(int i=0;i<m;i++)
    {
        if(n<c[i].a) {ans+=(ll)n*c[i].b;break;}
        ans+=(ll)c[i].a*c[i].b;
        n-=c[i].a;
    }
    printf("%lld\n",ans);
}

T2 第三题(three)

【问题描述】
Lamps-O-Matic 公司装修很大的吊灯。吊灯由几层组成。(从下至上)第一层的水晶灯直接挂在环上。把环集起来以后再挂到上一层的环上面,依此类推。最后一层是一个挂满了灯和小环的大环(废话)。现在由机器人来做挂灯的事。机器人身上有资源补给,挂灯的时候,它使用一个栈来存储灯和环。一开始,这个栈是空的。机器人执行一个指令集来运作。这个指令集是一个字符串。举例说明:aaaaa3aaa2aaa45。a 代表将小灯放入栈中。数字 N 代表将目前栈顶向下的 N 个资源取出,组成一个环,然后放回栈顶。整个栈的流程如下。
aaaaa → aa[aaa] → aa[aaa]aaa → aa[aaa]a[aa] → aa[aaa]a[aa]aaa →
aa[aaa]a[[aa]aaa]→END
这样就需要栈空间的最大值为 8,当其状态为 aa[aaa]a[aa]aaa 时就需要,注意:环和灯都算一个。
然而还有一个问题。机器人的栈不够大了(真是次品)。所以需要你编一个指令,使吊灯的设计不变,让指令所需的栈空间越小越好。所谓的设计不变,就是指:假设在同一层环原来有 4 个部件:i 1 ,i 2 ,i 3 ,i 4 ,现在可以改成 i 3 ,i 4 ,i 1 ,i 2 或是其他排列但是不能有部件增加或者减少。
【输入文件】
输入文件 three.in 每一行一个字符串,代表指令。
【输出文件】
输出文件为 three.out 包含一行,代表需要的栈空间
【输入样例】
aaaaa3aaa2aaa45
【输出样例】
6
【数据规模】
100%的数据字符串长度≤10 4
此题难度稍大,原题是poj 2161.但是这个题的原理很简单,就是不停地把每一大层拆成小部件,并寻找每个部件在组装过程中所需最大的栈空间.
这之后枚举每一个部件作为环的起点,求出该层栈空间.因此代码只是利用dfs写的模拟,要敢于去整理思路,并写下来.
代码如下:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<limits.h>
using namespace std;
int cnt,mdz[10005],len[10005];
char s[10005];
int dfs()
{
    int u=cnt--,zdz[10],v,t;
    if(s[u]=='a') len[u]=mdz[u]=1;
    else
    {
        len[u]=s[u]-'0';
        for(int i=len[u]-1;i>=0;i--) zdz[i]=dfs();
        mdz[u]=INT_MAX;
        for(int i=0;i<len[u];i++)
        {
            v=0;t=0;
            for(int j=i;j<len[u]+i;j++) {v=max(v,t+zdz[j%len[u]]);t++;}
            mdz[u]=min(mdz[u],v);
        }
    }
    return mdz[u];
}
int main()
{
    freopen("three.in","r",stdin);
    freopen("three.out","w",stdout);
    cin>>s;
    cnt=strlen(s)-1;
    cout<<dfs()<<endl;
}

T3 调整(tweak)

【问题描述】
已给定一个 N 个点 M 条边的有向图,点编号为 1 到 N,第 i 条边为(u i , v i ),权值为 w i 。你可以进行一次操作,使得任意一条边的权值变成任意非负整数。要求进行尽量少的操作次数,使得点 1 到点 N 的最短路径长度变成 c。题目保证,c 小于在未进行任何操作之前的原图中 1 到 N 的最短路长度。
【输入文件】
输入文件 tweak.in 第一行三个整数,N,M 和 c,接下来 M 行,每行一条边的信息 u i ,v i 和 w i ,第 i 行的表述第 i 条边的信息。保证不会有自环存在,对于不同的 i 和 j,(u i ,v i )不同于(u j ,v j )。
【输出文件】
输出文件 tweak.out 一行一个整数,要进行最少多少次操作才能使得最短路长度变为 c。
【输入样例】
3 3 3
1 2 3
2 3 3
1 3 8
【输出样例】
1
【样例说明】
将边 1,3 的权值修改为 3 就可以了。
【数据规模】
N≤100
M≤1000
0≤c≤100000
0≤w i ≤10000
30%数据满足 M≤20
50%的数据满足 M≤70
这题不说了...真心难.不过也很有水平.
代码如下:

#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<iostream>
using namespace std; 
int n,m,c,cur[105],num,x,y,w;
int l,r,d[105][100005],ans;
struct edge{
    int to,v,fa;
}a[50005];
struct queue{
    int z,y;
}q[5000005];
int main()
{

    freopen("tweak.in","r",stdin);
    freopen("tweak.out","w",stdout);
    scanf("%d%d%d",&n,&m,&c);
    for(int i=1;i<=n;i++) cur[i]=i;   
    num=n;
    for(int i=1;i<=m;i++)
      {
        scanf("%d%d%d",&x,&y,&w);
        num++;a[num].to=y;a[num].v=w;a[cur[x]].fa=num;cur[x]=num;
      }
    memset(d,-1,sizeof d);
    l=r=1;q[1].z=1;q[l].y=0;d[1][0]=0;
    while(l<=r)
      {
          int t=q[l].z;
          while(a[t].fa!=0)
            {
                  t=a[t].fa;
          int z=q[l].z,y=q[l].y,v=a[t].v,nd=d[z][y],xd=d[a[t].to][q[l].y+a[t].v];
                  if(z+v<=c && (nd<xd||xd==-1))
                  {
                    xd=nd;
                    r++; q[r].z=a[t].to; q[r].y=q[l].y+a[t].v; 

                  }
                  z=q[l].z;y=q[l].y;v=a[t].v;nd=d[z][y];xd=d[a[t].to][q[l].y+a[t].v];
          int d1=d[q[l].z][q[l].y],d2=d[a[t].to][q[l].y];
                  if(d1+1<d2||d2==-1)
                  {
                    d[a[t].to][q[l].y]=d2=d1+1;
                    r++;q[r].z=a[t].to;q[r].y=q[l].y;
                  }
            }
          l++;
      }
    ans=INT_MAX;
    for(int i=0; i<=c; i++) if(d[n][i]<ans && d[n][i]!=-1) ans=d[n][i];
    cout<<ans<<endl; 
}

rating after round #4: 1648.
本人有轻微的压行习惯,敬请谅解.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值