2016.8.6测试解题报告(ship,jsp,digital)

今天考的很惨烈啊……险些爆零,不过改的还是比较快的,今天上午的测试实在太水(c++语法基础题)就不发了。。。

1.友好城市

题目描述:
Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不相交的情况下,被批准的申请尽量多。

思路:
一开始我就写了一个并不靠谱的贪心,然后成功的成功的导致这道题就爆了零。这道题正解的思路其实就是一个线性dp——求最长不降子序列。如何把这道题转化为dp呢?
我们发现,所有符合题意的解都满足同一规律:若有四个城市可以互通,则设他们的坐标分别为x[1],x[2],x[3],x[4],y[1],y[2],y[3],y[4]。若每条道路互不相交,则必有x[i]和y[i]同排列递增。所以说当你以每对城市中处于北岸的城市排序既维持北岸有序时,只要南岸对应城市的坐标不降的序列就是可行的答案序列。

代码:

/*
2016.8.6 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n;
struct edge
{
    int x,y;
    edge() {x=y=0;}
}a[5005];
int sta[5005];
int len[5005];
int l=0;
bool cmp(edge a,edge b) {return a.x<b.x;}
int main()
{
    freopen("ship4.in","r",stdin);
    freopen("ship.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    sort(a+1,a+1+n,cmp);
    int maxlen=0;
    for(int i=1;i<=n;i++) len[i]=1;
    for(int i=n-1;i>=1;i--)
    {
        l=0;
        for(int j=i+1;j<=n;j++) if(a[i].y<a[j].y && len[j]>l) l=len[j];
        if(l>0) len[i]=l+1;
        maxlen=max(len[i],maxlen);
    }
    printf("%d",maxlen);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.作业调度方案

题目描述:
我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。

每个工件的每个工序称为一个操作,我们用记号j-k表示一个操作,其中j为1到n中的某个数字,为工件号;k为1到m中的某个数字,为工序号,例如2-4表示第2个工件第4道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。

例如,当n=3,m=2时,“1-1,1-2,2-1,3-1,3-2,2-2”就是一个给定的安排顺序,即先安排第1个工件的第1个工序,再安排第1个工件的第2个工序,然后再安排第2个工件的第1个工序,等等。

一方面,每个操作的安排都要满足以下的两个约束条件。
(1) 对同一个工件,每道工序必须在它前面的工序完成后才能开始;
(2) 同一时刻每一台机器至多只能加工一个工件。

另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。

由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为“1 1 2 3 3 2”。
还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。
例如,取n=3,m=2,已知数据如下:

工件号 机器号/加工时间
工序1 工序2
1 1/3 2/2
2 1/2 2/5
3 2/2 1/4

则对于安排顺序“1 1 2 3 3 2”,下图中的两个实施方案都是正确的。但所需要的总时间分别是10与12。
这里写图片描述
当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1)(2)的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1)(2)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。

显然,在这些约定下,对于给定的安排顺序,符合该安排顺序的实施方案是唯一的,请你计算出该方案完成全部任务所需的总时间。

思路:
纯模拟,特别注意处理细节!

代码:

/*
2016.8.6 BulaBulaCHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
struct point
{
    int pos;
    int num;
    point() {pos=0; num=0;}
};
bool cmp(point a,point b)
{
    return a.pos<b.pos;
}
int m,n;
int toto[25];
int seq[500];
int mix[500];
int mic[25][25];
int tim[25][25];
int start[25][25];
int tail[25][25];
int ans=0;
int main()
{
    freopen("jsp.in","r",stdin);
    freopen("jsp.out","w",stdout);
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m*n;i++)
    {
        int x;
        scanf("%d",&x);
        mix[i]=x;
        seq[i]=++toto[x];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            start[i][j]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&mic[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&tim[i][j]);
    for(int k=1;k<=n*m;k++)
    {
        int pos=mic[mix[k]][seq[k]];
        int t=tim[mix[k]][seq[k]];
        int st=0;
        int ed=t;
        int tot=0;
        static point node[50];
        for(int i=1;i<=k-1;i++)
        {
            if(mic[mix[i]][seq[i]]==pos)
            {
                node[++tot].pos=start[mix[i]][seq[i]];
                node[tot].num=i;
                node[++tot].pos=tail[mix[i]][seq[i]];
                node[tot].num=-i;
            }
        }
        sort(node+1,node+1+tot,cmp);
        int s=0;
        bool flag=0;
        for(int i=1;i<=tot;i++)
        {
            if(s==0 && node[i].pos-node[i-1].pos>=t)
            {
                if(node[i-1].pos>=tail[mix[k]][seq[k]-1])
                    {st=node[i-1].pos,flag=1; break;}
                else if(node[i].pos-tail[mix[k]][seq[k]-1]>=t)
                    {st=tail[mix[k]][seq[k]-1],flag=1; break;}
            }
            s+=node[i].num;
        }
        if(!flag) st=max(node[tot].pos,tail[mix[k]][seq[k]-1]);
        ed=st+t;
        start[mix[k]][seq[k]]=st;
        tail[mix[k]][seq[k]]=ed;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            ans=max(ans,tail[i][j]);
    printf("%d",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
/*
jsp.in
2 3
1 1 2 3 3 2
1 2
1 2
2 1
3 2
2 5
2 4

jsp.out
10
*/

3. 2^k进制数

题目描述:
设r是个2k 进制数,并满足以下条件:
(1)r至少是个2位的2k 进制数。
(2)作为2k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。
(3)将r转换为2进制数q后,则q的总位数不超过w。

在这里,正整数k和w是事先给定的。
问:满足上述条件的不同的r共有多少个?

我们再从另一角度作些解释:设S是长度为w 的01字符串(即字符串S由w个“0”或“1”组成),S对应于上述条件(3)中的q。将S从右起划分为若干个长度为k 的段,每段对应一位2k进制的数,如果S至少可分成2段,则S所对应的二进制数又可以转换为上述的2k 进制数r。

例:设k=3,w=7。则r是个八进制数(23=8)。由于w=7,长度为7的01字符串按3位一段分,可分为3段(即1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:

2位数:高位为1:6个(即12,13,14,15,16,17),高位为2:5个,…,高位为6:1个(即67)。共6+5+…+1=21个。

3位数:高位只能是1,第2位为2:5个(即123,124,125,126,127),第2位为3:4个,…,第2位为6:1个(即167)。共5+4+…+1=15个。
所以,满足要求的r共有36个。

思路:
它还是直接来自我的something I should know:
题目中的那个从另一角度分析就已经蕴含了这个题的基本思路。就以题目的例子为例,长度为7位的01字串按3位一段就这样分:0 000 000。其中除了首段,每段都小于(111)2,也即小于2k,而首段自然是小于2w%k(对于w%k为0时也成立)了。

如果首段为0,则当这个2k进制数位数分别为2、3、…、[n/k]时,如果用b_max表示2k,对应的解的个数分别为C[b_max-1][2]、C[b_max-1][3]、…、C[b_max-1][n/k](C[i][j]表示从i个数里选j个构成一组组合)。

如果首段不为0,设首段为x,则解就有c[b_max-x-1][n/k]个。

代码:

/*
2016.8.6 BulaBula CHN
*/
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int ans[220];
int f[1050][250];
int k,w;
void add(int*,int*);
int main()
{
    freopen("digital.in","r",stdin);
    freopen("digital.out","w",stdout);
    scanf("%d%d",&k,&w);
    if(w<=k) {printf("0\n");return 0;}
    int first,bitmax,lenth;
    bitmax=(1<<k)-1;
    //cout<<bitmax<<endl;
    if(w%k==0)
    {
        first=bitmax;
        lenth=w/k-1;
    }
    else
    {
        first=(1<<(w%k))-1;
        lenth=w/k;
    }
    f[1][0]=1;
    f[1][1]=1;
    ans[0]=0;
    //c[i][j]=c[i-1][j-1]+c[i-1][j]
    for(int i=1;i<=bitmax;i++)
    {
        for(int j=i+1;j>=1;j--) add(f[j],f[j-1]);
        if(i>=bitmax-first && i<bitmax) add(ans,f[lenth+1]);
    }
    for(int i=2;i<=lenth;i++) add(ans,f[i+1]);
    for(int i=ans[0];i>=1;i--) printf("%d",ans[i]);
    printf("\n");
    return 0;
}
void add(int a[],int b[])//´Óbµ½a
{
    int last=0;
    a[0]=max(a[0],b[0]);
    for(int i=1;i<=a[0];i++)
    {
        a[i]+=b[i]+last;
        last=a[i]/10;
        a[i]%=10;
    }
    if(last>0) a[++a[0]]=last;
    return;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值