今天考的很惨烈啊……险些爆零,不过改的还是比较快的,今天上午的测试实在太水(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;
}