本期主要讲解的与上期相同。
例题
T1
上课的时候调这个题感觉要吐了 \(qwq\)。。。
首先读入 \(n\) 行字符串,可以采取忽略中间无关单词的方式来直接读取 \(X\) 和 \(Y\)。
将所有名字存入 \(a\) 数组,对 \(a\) 数组按字典序排序后就可以开始 \(\text{DFS}\) 了,这里建议使用 next_premutation
。
设计一个 check
函数来判定当前全排列是否满足 \(n\) 条限制。具体实现:
-
遍历 \(n\) 条限制,对于第 \(i\) 条限制,在 \(8\) 个名字中找到对应的 \(X_i\) 和 \(Y_i\),保存它们的位置。
-
判断它们位置的绝对差是否 \(=1\)(相邻)即可。
#include<bits/stdc++.h>
using namespace std;
int n;
string x[8],y[8];
string ans[8]={"Beatrice","Belinda","Bella","Bessie","Betsy","Blue","Buttercup","Sue"};
int getpos(string x){
int p;
for(int i=0;i<8;i++)
if(ans[i]==x){
p=i; break;
}
return p;
}
bool check(){
for(int i=1;i<=n;i++){
int p1=getpos(x[i]),p2=getpos(y[i]);
if(abs(p1-p2)!=1) return 0;
}
return 1;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>x[i];
for(int j=1;j<=5;j++)
cin>>y[i];
}
do{
if(check()){
for(int i=0;i<8;i++) cout<<ans[i]<<'\n';
break;
}
}while(next_permutation(ans,ans+8));
return 0;
}
习题
T2
一个矩阵有主对角线(左上到右下)和副对角线(右上到左下),主对角线经过点的 \(x,y\) 坐标差一定,副对角线则是和一定。
于是我们可以记录三个 \(bool\) 数组 \(c,m,e\),分别记录在当前列 / 主对角线 / 副对角线是否能放置皇后。
在 \(\text{dfs(x)}\) 中我们定义行为格子,列为填入的数字,那么此题就变成了一个全排列问题。在 \(1 \sim n\) 遍历填入哪一列时,我们仅需判断 \(c_i,m_{x-i+n},e_{x_i}\) 是否均为 \(false\),若是则可以放置皇后。
注意回溯。
#include<bits/stdc++.h>
using namespace std;
int n,sum,ans[31];
int col[31],m[31],e[31];
void dfs(int x){
if(x==n+1){
sum++;
if(sum<=3){
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
cout<<'\n';
}
return;
}
for(int i=1;i<=n;i++){
if(!col[i]&&!m[x-i+n]&&!e[x+i]){
col[i]=m[x-i+n]=e[x+i]=1;
ans[x]=i;
dfs(x+1);
col[i]=m[x-i+n]=e[x+i]=0;
}
}
}
int main(){
cin>>n;
dfs(1);
cout<<sum;
return 0;
}
T3
考虑指数型枚举,即填格子时仅有填 / 不填两种选择,时间复杂度 \(O(2^n)\)。
在 \(\text{DFS}\) 函数中传入两个参数:\(x\) 和 \(tot\),分别记录即将填的格子数以及已经填完的格子数。
当 \(x=g+1\) 时,若符合要求且 \(tot <\) 全局答案,则更新全局答案并将当前选择方案 \(copy\) 给全局选择方案。
对于判断某一方案是否合法,则可以对于所有选择的种类,依次判断每一列之和是否 \(<\) 每头牛需要的维他命,若是则返回 \(1\);若均 \(\ge\) 所需维他命,则返回 \(1\)。
#include<bits/stdc++.h>
using namespace std;
int v,g,minn=1e9+31;
int ans[31],Ans[31];
int a[31],b[31][31];
bool check(int tot){
for(int j=1;j<=v;j++){
int sum=0;
for(int i=1;i<=tot;i++) sum+=b[ans[i]][j];
if(sum<a[j]) return 0;
}
return 1;
}
void dfs(int x,int tot){
if(x==g+1){
if(check(tot)){
if(tot<minn){
minn=tot;
for(int i=1;i<=minn;i++) Ans[i]=ans[i];
}
}
return;
}
ans[tot+1]=x;
dfs(x+1,tot+1);
dfs(x+1,tot);
}
int main(){
cin>>v;
for(int i=1;i<=v;i++) cin>>a[i];
cin>>g;
for(int i=1;i<=g;i++)
for(int j=1;j<=v;j++)
cin>>b[i][j];
dfs(1,0);
cout<<minn<<' ';
for(int i=1;i<=minn;i++) cout<<Ans[i]<<' ';
return 0;
}
T4
建立一个 \(b\) 数组保存下标,对 \(b\) 数组进行枚举全排列来遍历所有的顺序。
对于每一种顺序,遍历 \(n\) 个点,通过对上 / 下 / 左 / 右 / 离它最近的油的边界与它的距离取 \(\min\) 来得到在第 \(i\) 个点的最大半径,累加所有 \(n\) 格点的半径,用总面积减去就得到了剩余面积,对所有这样的剩余面积取 \(\min\) 即可。
注意精度问题。
#include<bits/stdc++.h>
using namespace std;
int n,ans=1e9+31;
double S,up,down,lft,rgt;
double x,y,xx,yy;
double rr[31];
struct node{
int a,b;
}p[31];
int id[31];
double dist(int aa,int bb){
return sqrt((p[aa].a-p[bb].a)*(p[aa].a-p[bb].a)+(p[aa].b-p[bb].b)*(p[aa].b-p[bb].b));
}
double surf(){
double sum=0.0;
for(int i=1;i<=n;i++){
double u=up-p[id[i]].b,d=p[id[i]].b-down,l=p[id[i]].a-lft,r=rgt-p[id[i]].a;
double t=1e9;
for(int j=i-1;j>=1;j--)
t=min(t,dist(id[i],id[j])-rr[j]);
if(t<0){ rr[i]=0; continue; }
rr[i]=min(u,min(d,min(l,min(r,t))));
sum+=rr[i]*rr[i]*3.1415926;
}
return sum;
}
int main(){
cin>>n;
cin>>x>>y>>xx>>yy;
for(int i=1;i<=n;i++){
cin>>p[i].a>>p[i].b;
id[i]=i;
}
up=max(y,yy),down=min(y,yy),lft=min(x,xx),rgt=max(x,xx);
S=(up-down)*(rgt-lft);
do{
double sf=surf();
ans=min(ans,(int)(round(S-sf)));
}while(next_permutation(id+1,id+n+1));
cout<<ans;
return 0;
}