装箱问题
题目描述
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入
第一行为一个整数,表示箱子容量;
第二行为一个整数,表示有n个物品;
接下来n行,每行一个整数表示这n个物品的各自体积。
输出
一个整数,表示箱子剩余空间。
样例输入
24
6
8
3
12
7
9
7
样例输出
0
- 这个题目运用到了dp(动态规划)。
- 我们首先需要开辟一个大小比v大一点的数组dp,这个数组用来模拟放置物品。
- 我们每一次得到要放进去的容量,从dp数组最后一个下标就也是v开始模拟,也就是j=v,是v是因为,我们最多放置v那么大的容量。
- 然后往前推算,这个box[i]到底放不放。推算结束条件是什么?就是我们前一次放的那个状态不能是小于0的,也就是j>=box[i]
- 前一次记录结果是存在dp[j-box[i]]中的(如果我们要拿这个box[i],我们就得看当容量为dp[j-box[i]]时取得的最优解),如果这个值加上box[i]的值大于这个位置的值,那么我就就拿这个box[i],否则就保持原来的值(就是一个拿和不拿问题)。
- 在我们重复5这个动作的时候,我们最终能保证dp[v]就是最大能拿的容量,那么v-dp[v]就是最小剩余的容量。
代码如下:
#include<stdio.h>
int dp[20005];//表示容积为v时可放置物品的容量
int max(int a,int b)
{
if(a>=b) return a;
return b;
}
int main()
{
int v,n,i,j;
scanf("%d%d",&v,&n);
int box[40] = {0};
for(i = 0;i<n;i++)
{
scanf("%d",&box[i]);
}
for(i = 0;i < n; i++)
{
//每一个试着判断放进去
for(j = v ;j >= box[i]; j--)
{
dp[j] = max(dp[j],dp[j-box[i]] + box[i]);//每次贪心放不放下这个box[i],每次都要保证时最优解,也就是最大能拿的。
//每次都从v往前面贪心,j-box[i]是在查看如果我们要放进去,那么剩下的容量的最优解
//如果放进去比本来大,就放进去,否则不放
}
}
printf("%d",v-dp[v]);//dp[v]这个元素会记下最终贪心的结果
return 0;
}
题目描述
农夫John要修理牧场的一段栅栏,他测量了栅栏,发现需要N(1<= N <= 20000)块木头,每块木头长度为Li(1<= Li <=50000)个单位。于是他购买了一条很长的、能锯成N块 的木头(即该木头长度是Li的总和)。农夫John忽略“损耗”,即忽略锯的时候产生锯末而造成的长度损耗。
农夫John没有锯子,于是他去找农夫Don,向他借锯子。
Don是一个守财奴,他不把锯子锯子借给John,而是要John为在木头上锯N-1次支付每一次锯的费用。锯一段木头要支付的费用等于这段木头的长度,即锯长度为21 的木头就要支付21美分。
例如,要将长度为21的木头锯成长度为8,5和8 的三段。
第一次锯木头花费为21,将木头锯成13和8;第2次锯木头花费13,将长度为13的木头锯成8和5;这样总的花费是21+13=34。如果将长度为21 的木头第一次锯成16和5的木头,则第二次锯木头要花费16,总的花费是37(大于34)。
Don让John决定在木头上切得次序和位置。请你帮助John确定锯N块木头索要花费的最少的钱。
输入
第1行: 一个整数N,表示木头的块数;
第2~N+1行: 每行给出一个整数,表示一段需要的木块长度。
输出
第1行: 一个整数,表示N-1次需要支付的最少费用。
样例输入 复制
3
8
5
8
样例输出 复制
34
修理牧场,锯栅栏
- 这个题目并不是很难,如果我们要求出最少的钱,那我们就要保证,我每次锯断的木头都要使接下来花费最小,那么到最后锯下来的一定是俩块最小的。
- 那么如何保证上述说法,排序。感觉走到哪里都离不开排序,我使用的是快排。
- 我们保证最小,那么我们就从最小的俩块木头开始,起初的花费就是俩块木头相加,我们要记下这个值,因为还要和接下来的木头相加组成那一次锯断的长度,也是锯断的费用。
- 总和sum都要加上t,t为前块木头和这一块木头的总和(相当于接合的过程啦)
代码如下:
#include<stdio.h>
int a[100020];
int qsort(int left,int right)
{
if(left>right) return 0;
int i,j,temp=a[left],t;
i=left,j=right;
while(i<j)
{
while(i<j&&a[j]>=temp) j--;
while(i<j&&a[i]<=temp) i++;
if(i<j)
{
t=a[i];a[i]=a[j];a[j]=t;
}
}
a[left]=a[i];
a[i]=temp;
qsort(left,i-1);
qsort(i+1,right);
}
int main()
{
int n,i,j;
unsigned long long sum=0,t;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
qsort(0,n-1);
sum=a[0]+a[1];
t=sum;
for(i=2;i<n;i++)
{
t+=a[i];
sum+=t;
}
printf("%llu\n",sum);
}
字符串的多样性(字符串处理)
题目描述
如果一个字符串中不同字符的个数小于等于字符串中每个字符的出现个数,我们称为这个字符串具有多样性
如字符串"334"具有多样性,其中不同字符的数量为2,'3'出现了2次,'4'出现了一次,满足字符串多样性的定义
字符串"77"就不满足,其中不同字符的数量为1,'7'出现了两次,不满足字符串多样性的定义
现在我们给定一个有n个元素的字符串a(‘0’ <= a[i] <= ‘9’)。
请问这个数组有多少个子串具有多样性, 子串是指由字符串删掉前n个字符和后m个字符得到的字符串(n,m可以为0,但是n + m < n);
元素相同,位置不同的子串被看作不同的。
输入
第一行输入一个整数n代表字符串的长度(n <= 1e5).
第二行输入一个字符串。
输出
输出一个整数表示有多少个子串具有多样性。
样例输入
6
399996
样例输出
10
提示
具有多样性的字串分别有:
3 出现一次
39 出现一次
399 出现一次
9 出现四次
96 出现一次
996 出现一次
6 出现一次
- 这个题目穷举即可。
- 就是从每一位开始往后穷举,到后面的每一位截至构成一个新字符串,然后判断是否满足条件即可。
- 但是我觉得上面这个还是麻烦了,于是又优化了一下。我们从每一位开始的地方,成为一个新的字符串。这个新字符串往后遍历到结束符’\0’为止,在新字符串每往后遍历一位,其实在遍历的过程中又算一个子串。
- 准备10个桶,边往后走,如果放进去那个桶的值为1,说明出现了新的字符。如果有一个桶里面的值大于了出现不同字符个数,说明这个字符串不具备多样性。
- 往后走的同时前面不同字符个数要保留,而且还要记录总的时候最大的桶的值,往后面一直刷新最大桶的值。判断这个最大桶是否大于不同字符的个数即可。
代码如下:
#include<stdio.h>
#define N 100010
char a[N];
long long seek(char s[])
{
int a[15]={0},i,j,count=0,max=0;
long long sum=0;
for(i=0;s[i];i++)
{
a[s[i]-'0']++;
if(a[s[i]-'0']==1)
{
count++;
}
if(max<a[s[i]-'0']) max=a[s[i]-'0'];
if(max<=count) sum++;
}
return sum;
}
int main()
{
int n,i;
long long sum=0;
scanf("%d\n",&n);
gets(a);
for(i=0;a[i];i++)
{
sum+=seek(a+i);
}
printf("%lld\n",sum);
return 0;
}
分小球(C语言)
题目描述
现在有n个小球分给三个学长,每个小球都是一样的,学长至少会拿一个小球,现在学长告诉你有多少种分配方法。
我们可以用隔板法来思考:将10个球排成一排,球与球之间形成9个空隙,将两个隔板插入这些空隙中(每空至多插一块隔板),隔板会将球分为3个部分。分配方案会是C(n-1,m-1)=C(9,2)=36(个)。
但是学长觉得这个问题太简单了,他想问你学长可以不拿球的分配方案。
保证n个球一定被分完
输入
输入一个整数n (3 <= n <= 1e4)
输出
输出一个整数表示方案的个数
样例输入
3
样例输出
10
- 这个题目没有那么复杂,我找到了规律。
- 题目已经说了隔板法,这难道不是在暗示我们用隔板法嘛!
- 因为学长能不拿球嘛,那我们在它原有给的球里面加俩个球,给这学长,分配完之后,拿回来。那么就有学长不拿球了。(就是把学长拿一个球的情况看成不拿球的情况)
这是组合的公式。然后这道题我们可以简化成(字写得不是很好看)
.
- 然后要多加俩个球就是把n+2带入n中.传递参数的时候+2即可。
代码如下:
#include<stdio.h>
unsigned long long slove(int n)
{
return n*(n-1)/2;
}
int main()
{
int n;
unsigned long long s=0;
scanf("%d",&n);
s=slove(n+2);
printf("%llu\n",s);
}
Cool Number
题目描述
对于一个数大于等于0的数X,我们定义三个函数,g1(x),g2(x),f(x)
g1(x) = 让x的所有数字从大到小排列(比如123变成321)
g2(x) = 让x的所有数字从小到大排列(比如321变成123)
f(x) = g1(x) - g2(x)
我们给定两个数N和K,让你算出N,f(N)经过K次运算后是多少?
输入
第一行输入两个数N(1<=N<=1e9),K(1<=k<=1e5)
输出
一个数f(N)
样例输入 复制
314 2
样例输出 复制
693
- 这个题目穷举就可以解决,因为1<=N<=1e9,是在整型范围内的,我是用了long long(以防万一,开大点没什么关系)。变成数组升序排序数字,然后转成数字,变成数组降序排序数字,转成数字,然后相减,得到一个新的数,再循坏往下。
- 优化一下。先排序,再逆序。值得注意的一点是一定是要先排降序转成数字,再逆序。如果先排升序,那么0就会排到前面去,转成逆序数字时就会出错。
代码如下:
#include<stdio.h>
long long g1(long long m)//求
{
long long i=0,j,t,a[20],n;
long long s=0,k=m;
while(k)
{
a[i++]=k%10;
k/=10;
}
n=i;
for(i=0;i<n-1;i++)
{
for(j=n-1;j>i;j--)
{
if(a[j]>a[j-1])
{
t=a[j];a[j]=a[j-1];a[j-1]=t;
}
}
}
for(i=0;i<n;i++)
{
s=s*10+a[i];
}
return s;
}
long long fs(long long n)
{
long long a=n,s=0;
while(a)
{
s=s*10+a%10;
a/=10;
}
return s;
}
int main()
{
long long n,k,i;
long long a,b,s;
scanf("%lld%lld",&n,&k);
s=n;
while(k--)
{
a=g1(s);
b=fs(a);
s=a-b;
}
printf("%lld\n",s);
}