您的位置:首页 > 其它

清华大学07年复试上机题题解

2010-03-19 18:51 253 查看
第一题,质因数分解问题

由于数据规模不大,所以完全可以使用试除法,从2开始,每次找到一个因子后,nt /= i,并把试除起点重新设置为2,这样可以保证所有找到的因子不可能为合数.因此还避免了素数判定。

#include<iostream>
using namespace std;

int f(int n)
{
int nt = n;
int cnt = 0;
for(int i = 2; i <= nt;i++)
if(!(nt%i)){
cout<<i<<" ";
cnt++;
nt = nt/i;
i = 1;
}
cout<<endl;
return cnt;
}

int main()
{
int n;
while(cin>>n){
cout<<f(n)<<endl;
}
return 0;
}


第二题,大数的二进制十进制互相转换

本题包括的两个子问题如下:

1.十进制大数串转换为二进制的问题,实际上是大数除以一个int型数据的问题,这比大数除大数的除法要简单得多,可以直接使用小学时学过的试除法,

即从高位开始,每位除以2,当然,要记得加上上一次除剩下来的结果。

2.二进制串转换为十进制。由于是大数,这里求2的幂的次数非常高,我的做法便是使用类似于求任意位阶乘的方法求出2的任意次幂,然后再用大数加法。这种做法是最原始的,但由于数学知识有限,只能如此了。

#include<iostream>
using namespace std;

const int N = 1000;
const int M = 5000;

char a
;//用于存储输入的初始数和最终结果
int  b[M];//存储二进制位
int  c
;//存储2^N次方的临时值
int alen,blen,clen;

//本函数实现计算2^s次,并把结果与a相加存储于a
void plus(int s)
{
memset(c,0,sizeof(c));
c[0]=1;
clen = 1;
//计算2^s次方,循环执行s次乘法
for(int i = 0;i < s;i++){
for(int j = 0;j < clen;j++)
c[j] *= 2;
//处理进位信息
for(int k = 0;(k < clen)||(c[k] != 0);k++){
c[k+1] += (c[k]/10);
c[k] = c[k]%10;
}
clen = k;
}

int alen = (clen > alen?clen:alen)+1;//可能有进位,所以先把长度加1
//大数加法
for(int v = 0;v < alen;v++)
{
a[v] += c[v];
a[v+1] +=(a[v]/10);
a[v] %= 10;
}
}

void div2bit()
{
alen = strlen(a);
for(int i = 0; i < alen;i++)
a[i] = a[i]-'0';

int start = 0;
blen = 0;
while(a[alen-1] != 0 || start < alen){
//最末位的奇偶性便可决定模2的结果
if(a[alen-1]%2==1)b[blen++] = 1;
else b[blen++] = 0;

int r = 0;//上一次除2时的余
for(int j = start;j < alen;j++){
int tmp = r*10 + a[j];
a[j] = tmp/2;
r = tmp%2;
}
//高位为0时可让下一次试除的起始位后移
if(a[start]==0)start++;
}

memset(a,0,sizeof(a));
for(int s = 0; s < blen;s++)
if(b[s]!=0)
plus(blen-s-1);
//处理高位为0的情况
while(a[alen-1]==0)alen--;
}

int main()
{
while(cin>>a){
div2bit();
for(int j = alen-1;j >=0;j-- )
cout<<(a[j]-0);
cout<<endl;
}
return 0;
}


第三题,邮票面值问题

这个实际上是经典的0-1背包问题,状态转移方程基本上可以不做任何修改。

不过有个值得注意的问题是:

对于背包问题,如果只求最优解,而无需要恰好装满背包,则把全部初始化为0。

若要求刚好装满背包,则需要做如下初始化:

for(i = 0;i <= m;i++)
c[i][0] = fail; //因为需要装满,所以不选就无法装满
//c[i][0] = fail; //若不需装满,0就是最优解
for(i = 0;i <= n;i++)
c[0][i] = 0; //装满为零的都是有解的,就是选零张


这种差别很微妙,需要认真体会一下。

#include<iostream>
using namespace std;

#define fail  10000

const int M = 200;
const int N = 20;

int c[M]
;//c[i][j]表示面额为i,使用前j张面值时的最少张数
int v
; //每张邮票的面值
int m = 0,n = 0;
int dp()
{
int i,j;
for(i = 0;i <= m;i++)
c[i][0] = fail;
for(i = 0;i <= n;i++)
c[0][i] = 0;

int min ;
for(i = 1; i <= m;i++){
for(j = 1; j <= n;j++ ){
if(i >= v[j]) {
c[i][j] = c[i][j-1];
if(c[i][j] > c[i-v[j]][j-1]+1)c[i][j]=c[i-v[j]][j-1]+1;
} else c[i][j] = c[i][j-1];
}
}
if(c[m]
==fail)c[m]
= 0;
return c[m]
;
}

int main()
{
memset(c,0,sizeof(c));
memset(v,0,sizeof(v));
while(cin>>m>>n){
for(int i = 1;i <= n;i++)
cin>>v[i];
cout<<dp()<<endl;
memset(c,0,sizeof(c));
memset(v,0,sizeof(v));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: