2019_GDUT_新生专题I选集 DFS
D 题意:我有N块宝石,并计划使用其中的K块为我的母亲制作项链,但她不会接受太重的项链。 考虑到每种宝石的价值和重量,请帮助我找出母亲会接受的最有价值的项链。
(N <= 20) (K <= N) 每块宝石的价值和重量都小于等于1000.
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2660
第一眼看上去像是01背包问题,但是由于N的范围很小只有20,所以可以直接暴力搜索,把所有情况都找出来再判断是否符合要求并记录最优解即可。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int maxn,ans,k,n,weight,sum;
int v[25],w[25];
void dfs(int x,int i){
if (x==k||i==n+1)
{
if (weight<=maxn) ans=max(ans,sum);
return;
}
weight+=w[i];
sum+=v[i];
dfs(x+1,i+1);
weight-=w[i];
sum-=v[i];
dfs(x,i+1);
return;
}
int main(){
int t;
scanf("%d",&t);
while (t--){
scanf("%d %d",&n,&k);
memset(v,0,sizeof(v));
memset(w,0,sizeof(w));
ans=0;weight=0;sum=0;
for (int i=1;i<=n;i++)
scanf("%d %d",&v[i],&w[i]);
scanf("%d",&maxn);
dfs(0,1);
printf("%d\n",ans);
}
}
E 题意:乔治拿了相同长度的木棍,随机切开,直到所有木棍的长度最长小于等于50个单位。 现在,他想将木棍恢复到原始状态,但他忘记了自己原来拥有多少根木棍以及它们最初有多长。 请帮助他,设计一个程序,计算出那些棍子的最小可能的原始长度。 所有以单位表示的长度都是大于零的整数。
题目链接:http://poj.org/problem?id=1011
因为木棍的总长是不会变得,那么只需要枚举总长度的各个因子,判断当前长度是否符合题目要求即可,前面都是一些简单的数学问题,这道题目的难点在于怎么利用dfs来判断所有的木棍是否能恢复成某个长度的木棍。
木棍最多会有64根,显然直接搜索是会超时的,所以要进行优化。
第一重剪枝:如果我在第一次搜索的时候选择某种长度的木棍得到的结果是该种长度的木棍无法使得最终拼接成功,那么我就不需要再次搜索这种长度的木棍。
第二重剪枝:限制拼接用的木棍为递减序列(对于拼接出来的木棍来说先拼长度为X的木棍再拼长度为Y的木棍 与 先拼长度为Y的木棍再拼长度为X的木棍,这两种情况是一样的,所以只需要搜索一遍就好)
第三重剪枝:如果在一根原始木棒中尝试拼接的第一根木棍的递归分支就以失败返回,直接判断当前分支无解。
第四重剪枝:如果两个木棍的长度和与一个木棍的一样,只需要尝试一个即可,因为两根木根显然要比一根灵活。
第五重剪枝:优先尝试较长的木棍,优化搜索顺序。
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int cnt,len,n;
int v[1000]={0},a[1000];//v数组记录当前木棍是否被使用过
bool cmp(int x,int y){
return x>y;
}
//k为正在拼接的木棍,cab为当前正在拼接的木棍长度,last为上一次所选择的木棍
bool dfs(int k,int cab,int last){
if (k>cnt) return true;//如果拼接成功k根木根则该方案成立
if (cab==len) return dfs(k+1,0,1);//如果当前木根拼接成功则开始拼接下一木棍
int fail=0;//用于记录上一次尝试拼接的木棍
for (int i=last;i<=n;i++)
{
if (v[i]==0&&fail!=a[i]&&cab+a[i]<=len)
{
v[i]=1;
if (dfs(k,a[i]+cab,i+1)) return true;
v[i]=0;
fail=a[i];//状态回调并记录已尝试的木棍
if (cab==0||cab+a[i]==len) return false;//上述题解中的第三第四重优化。
}
}
return false;
}
int main(){
while (scanf("%d",&n)){
if (n==0) break;
int sum=0;
memset(v,0,sizeof(v));
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a+1,a+1+n,cmp);//由大到小排序,优化搜索顺序
for (cnt=n;cnt>=1;cnt--)
{
if (sum%cnt) continue;
len=sum/cnt;
if (dfs(1,0,1))
{
printf("%d\n",len);
break;
}
}
}
}
F 题意:给定一个正整数n,请编写一个程序来寻找n的一个非零的倍数m,这个m应当在十进制表示时每一位上只包含0或者1。你可以假定n不大于200且m不多于100位。 输出任一符合题意的m。
题目链接:http://poj.org/problem?id=1426
虽然题目说是不多于100位,但是实际上m的范围是不会超过long long的
所以直接暴力搜每一位数是0还是1就行
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int flg;
long long n;
void dfs(int k,long long x){
if (flg||k>19) return;
if (x%n==0)
{
flg=1;
printf("%lld\n",x);
return;
}
dfs(k+1,x*10);
dfs(k+1,x*10+1);
}
int main(){
while (scanf("%lld",&n)){
if (n==0) break;
flg=0;
dfs(1,1);
}
return 0;
}