2016.9.17测试解题报告

这套题真水。

1.引爆炸弹

问题描述:
有n个炸弹,有些炸弹牵了一根单向引线(也就是说引线只有在这一端能被炸弹点燃),只要引爆了这个炸弹,用引线连接的下一个炸弹也会爆炸。每个炸弹还有个得分,当这个炸弹被引爆后就能得到相应得分。
现在要你引爆k个炸弹,使得分最大。

输入说明:
第1行两个整数n、k。
接下来n行每行两个整数a[i]、b[i]。a[i]表示这个炸弹用引线连接的下一个炸弹,如果a[i]为0,则表示这个炸弹没连接引线。b[i]表示这个炸弹的得分。

输出说明:
仅一个数,表示最大得分。

思路:
很容易看出题目给出的是一个树状的结构。所以分析问题的时候可以把给出的单向边反过来分析。每一次都从叶节点开始向上更新,一直更新到根结点的f值(即该结点开始的链的最大权值)和g值(即最大链中该节点所连接的子节点的序号)。然后在引爆的时候,从根节点开始向下引爆(利用刚刚处理出来的变量g),遇到已经被炸的结点就直接跳过。引爆k个节点后统计答案输出(long long)。

代码:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
struct Node
{
    int num;
    int pre;
    int g;
    long long f;
    int v;
    int outdge;
    bool u;
    Node() {pre=g=f=outdge=v=u=0;}
}a[200005];
int pos[200005];
int ans_cnt=0;
long long ans=0;
bool cmp(Node a,Node b)
{
    return a.f>b.f;
}
int n,k;
//性质:如果一个点没有被选过,那么它的最优子节点链肯定没有被选过
int main()
{
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].pre,&a[i].f);
        a[i].v=a[i].f;
        a[a[i].pre].outdge++;
        a[i].num=i;
    }
    for(int i=1;i<=n;i++) if(!a[i].outdge)
    {
        int pos=i;
        while(a[pos].pre)
        {
            if(a[a[pos].pre].f<a[pos].f+a[a[pos].pre].v)
            {
                a[a[pos].pre].f=a[pos].f+a[a[pos].pre].v;
                a[a[pos].pre].g=pos;
            }
            pos=a[pos].pre;
        }
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++) pos[a[i].num]=i;
    for(int i=1;i<=n;i++) if(!a[i].u)
    {
        ans_cnt++;
        ans+=a[i].f;
        if(ans_cnt==k) break;
        int po=pos[a[i].num];
        while(po)
        {
            a[po].u=1;
            po=pos[a[po].g];
        }
    }
    printf("%I64d",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.取石子

问题描述:
有n个石子围成一圈,每个石子都有一个权值a[i],你需要取一些石子,每个石子的得分是a[i]*d,d表示这个石子到两边被取了的石子的距离和。现在你可以取若干石子,使得分最大。

输入说明:
第1行一个整数n。
接下来n行,每行一个整数a[i]。

输出说明:
仅一个整数,表示最大得分。

思路:
由数学证明可知,得出最大解的方案只可能是拿走一个石子或者两个石子。计算即可。

代码:

#include<cstdio>
#include <iostream>
using namespace std;
int n;
long long fir=0,sec=0;
int main()//数学题
{
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x>=fir) sec=fir,fir=x;
        else if(x>sec) sec=x;
    }
    if(fir>(n-2)*sec) printf("%I64d",fir*(n-1));
    else printf("%I64d",(fir+sec)*(n-2));
    fclose(stdin);
    fclose(stdout);
    return 0;
}

3.化工厂装箱员

问题描述:
118号工厂是世界唯一秘密提炼锎的化工厂,由于提炼锎的难度非常高,技术不是十分完善,所以工厂生产的锎成品可能会有3种不同的纯度,A:100%,B:1%,C:0.01%,为了出售方便,必须把不同纯度的成品分开装箱,装箱员grant第1次顺序从流水线上取10个成品(如果一共不足10个,则全部取出),以后每一次把手中某种纯度的成品放进相应的箱子,然后再从流水线上顺序取一些成品,使手中保持10个成品(如果把剩下的全部取出不足10个,则全部取出),如果所有的成品都装进了箱子,那么grant的任务就完成了。
由于装箱是件非常累的事情,grant希望他能够以最少的装箱次数来完成他的任务,现在他请你编个程序帮助他。

输入说明:
第1行为n(1<=n<=100),为成品的数量
以后n行,每行为一个大写字母A,B或C,表示成品的纯度。

输出说明:
仅一行,为grant需要的最少的装箱次数。

思路:
一眼动规。设状态f[num1][num2][num3][i]为手中由num1个A,num2个B,num3个C,一共现在处理了i个产品时候的最少次数。预处理出各个区间各产品的个数可有状态转移方程:

  • f[numa[i+1][i+num1]][num2+numb[i+1][i+num1]][num3+numc[i+1][i+num1]][i+num1]
  • f[num1+numa[i+1][i+num2]][numb[i+1][i+num2]][num3+numc[i+1][i+num2]][i+num2]
  • f[num1+numa[i+1][i+num3]][num2+numb[i+1][i+num3]][numc[i+1][i+num3]][i+num3]

代码:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n;
int num[205][205][4];
int f[25][25][25][205];
void print(int i,int num1,int num2,int num3)
{
    cout<<"qudiao A "<<num[i+1][i+num1][1]<<" "<<num2+num[i+1][i+num1][2]<<" "<<num3+num[i+1][i+num1][3]<<" "<<i+num1<<" "<<f[num[i+1][i+num1][1]][num2+num[i+1][i+num1][2]][num3+num[i+1][i+num1][3]][i+num1]<<endl;
    cout<<"qudiao B "<<num1+num[i+1][i+num2][1]<<" "<<num[i+1][i+num2][2]<<" "<<num3+num[i+1][i+num2][3]<<" "<<i+num2<<" "<<f[num1+num[i+1][i+num2][1]][num[i+1][i+num2][2]][num3+num[i+1][i+num2][3]][i+num2]<<endl;
    cout<<"qudiao C "<<num1+num[i+1][i+num3][1]<<" "<<num2+num[i+1][i+num3][2]<<" "<<num[i+1][i+num3][3]<<" "<<i+num3<<" "<<f[num1+num[i+1][i+num3][1]][num2+num[i+1][i+num3][2]][num[i+1][i+num3][3]][i+num3]<<endl;
}
int main()
{
    freopen("worker.in","r",stdin);
    freopen("worker.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        char c[5];
        scanf("%s",c+1);
        int flag=c[1]-'A'+1;
        for(int st=1;st<=i;st++)
            for(int ed=200;ed>=i;ed--)
                 num[st][ed][flag]++;
    }
    /*
    for(int k=1;k<=3;k++)
    {
        for(int i=1;i<=11;i++)
        {
            for(int j=1;j<=11;j++)
                cout<<num[i][j][k]<<" ";
            cout<<endl;
        }
        cout<<endl;
    }*/
    memset(f,56666,sizeof f);
    f[num[1][10][1]][num[1][10][2]][num[1][10][3]][10]=0;
    for(int i=10;i<=n+50;i++)
        for(int num1=0;num1<=10;num1++)
            for(int num2=0;num2<=10;num2++)
                for(int num3=0;num3<=10;num3++)
                    if(f[num1][num2][num3][i]<10086)
    {
        if(num1==0 && num2==0 && num3==0)
        {
            printf("%d",f[num1][num2][num3][i]);
            return 0;
        }
        //cout<<num1<<" "<<num2<<" "<<num3<<" "<<i<<" "<<f[num1][num2][num3][i]<<endl;
        f[num[i+1][i+num1][1]][num2+num[i+1][i+num1][2]][num3+num[i+1][i+num1][3]][i+num1]=min(f[num[i+1][i+num1][1]][num2+num[i+1][i+num1][2]][num3+num[i+1][i+num1][3]][i+num1],f[num1][num2][num3][i]+1);
        f[num1+num[i+1][i+num2][1]][num[i+1][i+num2][2]][num3+num[i+1][i+num2][3]][i+num2]=min(f[num1+num[i+1][i+num2][1]][num[i+1][i+num2][2]][num3+num[i+1][i+num2][3]][i+num2],f[num1][num2][num3][i]+1);
        f[num1+num[i+1][i+num3][1]][num2+num[i+1][i+num3][2]][num[i+1][i+num3][3]][i+num3]=min(f[num1+num[i+1][i+num3][1]][num2+num[i+1][i+num3][2]][num[i+1][i+num3][3]][i+num3],f[num1][num2][num3][i]+1);
        //print(i,num1,num2,num3);
    }
    printf("%d",f[0][0][0][n]);


    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值