——————————————————————————————————20181101
2592 cost數
2107 某種密碼
3749 遞增數列
3207 木棒分組
【以上題目均出自WOJ】
T1 cost數
容斥原理
數學推理
GCD【最大公因數】
LCM【最小公倍數】
20%
直接暴力
60%
根據容斥原理,三個的時候很好推
100%
容斥原理可以加到n
- 這時我們需要用個dfs枚舉每個ai選與不選,
- 若選出k個數,且k爲奇數,則ans加上m/LCM(選出的數),若爲偶數ans減去m/LCM(選出的數)。
- 當然,還要加上優化,包括可行性剪枝(選出的數的LCM要小於等於m。由於ai>17ai>17,該剪枝十分有效)、優化搜索順序(先搜大數)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,a[40],ans=0;
int gcd(int x,int y){
int t;
if(x<y)t=y,y=x,x=t;
while(y){
t=x%y;
x=y;
y=t;
}
return x;
}
ll lcm(int x,int y){
return 1ll*x/gcd(x,y)*y;
}
void dfs(int pos,ll l,int k){
if(l>m)return;
if(pos>n){
if(k%2){
ans+=m/l;
}
else if(k)ans-=m/l;
return;
}
dfs(pos+1,l,k);
ll r=lcm(l,a[pos]);
if(r<=m)dfs(pos+1,r,k+1);
}
inline int Sort(int x,int y){
return x>y;
}
int main(){
scanf("%d%d",&n,&m);
m-=17;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1,Sort);
dfs(1,1,0);
printf("%d",ans+1);
return 0;
}
T2 某種密碼
DFS
折半搜索
STL
60%
直接暴力每一個選不選
100%
其實這道題是一個01揹包,但容積太大,無法開數組,時間也受不了。
看在物品少,只有40的情況下,就只有暴搜了。
先將物品分爲兩半A,B,按 60% 的方法搜索,並記錄。
對集合A暴力枚舉其所有子集中元素和並存入哈希表(可重集),
再對集合B暴力枚舉每個子集的元素和s,
同時查找哈希表中值爲(key−s)的元素個數並計數。
時間複雜度爲O(2n/2)。
如果用STL的話,可以用
#include<tr1/unordered_map>
using namespace tr1;
tr1::unordered_map<long long,int>Map;
比較快。
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
using namespace tr1;
#define ll long long
int n,a[50];
ll k,ans=0;
tr1::unordered_map<ll,int>s;
void dfs1(int pos,int mx,ll sum){
if(pos>mx){
s[sum]++;
return;
}
dfs1(pos+1,mx,sum+a[pos]);
dfs1(pos+1,mx,sum);
}
void dfs2(int pos,int mx,ll sum){
if(pos>mx){
if(s.find(k-sum)!=s.end())ans+=s[k-sum];
return;
}
dfs2(pos+1,mx,sum+a[pos]);
dfs2(pos+1,mx,sum);
}
int main(){
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dfs1(1,n/2,0);
dfs2(n/2+1,n,0);
printf("%lld",ans);
return 0;
}
T3 遞增數列
DFS
迭代加深
因爲深度是的,所以可以用迭代加深,每次都控制一下深度。
第一次只能遞歸1層,如果沒有搜索到,限制遞歸2層,如果還沒有搜索到,就繼續往下遞增,直到搜到爲止。
- 每次我們最多增大兩倍,如果增大2r還沒到就不行。
- 不能超過我們要找的數。
#include<bits/stdc++.h>
using namespace std;
int m,h,ans[1000];
bool dfs(int x,int dep){
if(dep>h)return 0;
if(x<<(h-dep)<m)return 0;
if(x>m)return 0;
if(x==m)return 1;
ans[dep]=x;
for(int i=dep;i>=0;i--)
if(dfs(x+ans[i],dep+1))return 1;
return 0;
}
int main(){
scanf("%d",&m);
for(h=0;;h++){
if(dfs(1,0))break;
}
printf("%d\n",h+1);
for(int i=0;i<h;i++)
printf("%d ",ans[i]);
printf("%d",m);
return 0;
}
T4 木棒分組
DFS
搜索剪枝
洛谷【有點不同】
70%
設有個最終的木棒,
枚舉每一個小木棒在每一個大木棒的情況,最後一下。
時間複雜度: O ( nn )
100%
題解: * _ *
#include<bits/stdc++.h>
using namespace std;
int a[60],n,mx=0,all=0,cnt,len,vis[60];
inline int Sort(int x,int y){
return x>y;
}
bool dfs(int use,int cab,int pos){
//多少個已拼好 當前根拼了多少 上一次用的什麼
if(use>cnt)return 1;
if(cab==len)return dfs(use+1,0,1);
int last=0;
for(int i=pos;i<=n;i++){
if(!vis[i]&&cab+a[i]<=len&&last!=a[i]){
vis[i]=1;
if(dfs(use,cab+a[i],i+1))return 1;
last=a[i];
vis[i]=0;
if(!cab||cab+a[i]==len)return 0;
}
}
return 0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>mx)mx=a[i];
all+=a[i];
}
sort(a+1,a+n+1,Sort);
for(len=mx;len<=all/2;len++){
if(all%len)continue;
cnt=all/len;//cnt根
memset(vis,0,sizeof(vis));
if(dfs(1,0,1)){
printf("%d",len);
return 0;
}
}
printf("%d",all);
return 0;
}