A- 收集纸片(暴力+全排列)
题意: 给出k个纸片所在的x,y,还有起点sx,sy,问从起点开始收集全部纸片并且回到起点的路径最短是多少。
题解: 因为数据很小,可以直接全排列搜索一遍取最小值。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
typedef long long ll ;
using namespace std ;
const int N = 1e5+10 ;
const int M = 1e5+10 ;
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}} ;
int n,m,sx,sy,k,res ;
int a[20] ,x[20],y[20] ;
int dis(int x1,int y1,int x2,int y2){
return abs(x1-x2)+abs(y1-y2) ;
}
void solve(){
for(int i=1 ; i<=k ; ++i) a[i]=i ;
do{
int ans=dis(sx,sy,x[a[1]],y[a[1]]) ;
for(int i=2 ; i<=k ; ++i)
ans+=dis(x[a[i-1]],y[a[i-1]],x[a[i]],y[a[i]]) ;
ans += dis(x[a[k]],y[a[k]],sx,sy) ;
res = min(res,ans) ;
}while(next_permutation(a+1,a+1+k)) ;
}
int main(){
int t ; scanf("%d",&t) ;
while(t--){
scanf("%d%d",&n,&m) ;
scanf("%d%d",&sx,&sy) ;
scanf("%d",&k) ;
for(int i=1 ; i<=k ; ++i) scanf("%d%d",&x[i],&y[i]) ;
res = 0x3f3f3f3f ;
solve() ;
printf ("The shortest path has length %d\n",res) ;
}
return 0 ;
}
B-生日蛋糕(dfs+剪枝+几何计算)*
题意 给出一个由m个圆柱体搭成的蛋糕,体积为nπ,并且从上到下的圆柱的半径和高度是递增的,求出该蛋糕的最小面积sπ中的s。
题解:
①、先确定每个圆柱的最小体积和面积,由于圆柱是从上至下递增的,所以半径和高度最小为i,最上面为第1层,先计算出最小体积和面积。
②、从下至上逐层搜索,每一层的半径和高度从大到小枚举。假设当前已经记录的体积为v面积为s,则当前层最大的半径为,剩余体积n-v开根号,n-v=
∑
i
=
1
d
\sum_{i=1}^{d}
∑i=1dRi2*Hi,但是第d层的半径必须比d+1层的半径小,所以第d层的最大半径为两个可能性取最小值,即min(sqrt(n-v),r[d+1]-1) , 然后前面也分析过了第d层的半径最小为d。
③、确定了枚举的半径,就可以确定要枚举的直径,因为n-v=
∑
i
=
1
d
\sum_{i=1}^{d}
∑i=1dRi2*Hi,所以高度h为(n-v)/i * i,同样因为第d层的高度小于d+1层的高度,所以两者取小
④、确定剪枝条件,前面预处理的miv和mis,如果当前的体积或面积加上最小体检面积大于给定的体积或者是最佳答案就可以剪枝,还有就是 s+2(n-v)/i这个剪枝条件,这个我看了别人题解是这样理解的,体积V=R×R×H , S=2×R×H ,那么S=2×V/R , 当前增加的面积+前面搜索的面积大于最优值就可以剪枝。
参考博文:参考一、参考二
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
typedef long long ll ;
using namespace std ;
const int N = 30 ;
const int INF=0x3f3f3f3f ;
int n,m,ans,v,s;
int miv[N],mis[N],r[N],h[N];
void dfs(int d){
// printf ("d=%d s=%d v=%d\n",d,s,v) ;
if(!d){
if(v==n&&ans>s) ans=s ;
return ;
}
//如果当前层的最小体积/面积比给定的体积n或者算出的ans还大就不用继续往下搜索(剪枝)
if(v+miv[d]>n) return ;
if(s+mis[d]>ans) return ;
for(int i=min((int)sqrt(n-v),r[d+1]-1) ; i>=d ; --i){//枚举第d层的半径i
for(int j=min((n-v)/(i*i),h[d+1]-1) ; j>=d ; --j){//枚举第d层的高度j
//if(s+2*i*j>ans) return ;
if (s+2*(n-v)/i>ans) return;//当前面积加上当前层增加的面积大于ans
r[d]=i , h[d]=j ;
v+=i*i*j , s+=2*i*j ;
if(d==m) s+=i*i ; //如果是底层要加上顶面的面积
dfs(d-1) ;
v-=i*i*j , s-=2*i*j ;
if(d==m) s-=i*i ;
}
}
}
int main(){
scanf("%d%d",&n,&m) ;
for(int i=1 ; i<=m ; ++i){
//从上往下计算每个蛋糕最少增加的体积和面积
miv[i] = miv[i-1]+i*i*i ;
mis[i] = mis[i-1]+2*i*i ;
}
r[m+1]=INF,h[m+1]=INF,ans=INF ;
dfs(m) ;
if(ans<INF) printf("%d\n",ans) ;
else printf("0\n") ;
return 0 ;
}
C- Rabbit的工作(1)(动态规划)
题意: 给出一串01字符串,1时可以选择工作或不工作,工作第一天消耗1点体力,第二天消耗2,第三天消耗3,依次类推,兔子不想消耗的总体力超过k,问她最多能工作几天。
题解: dp[j][k] 表示第i天前工作了j天,并且连续工作了k天的最小消耗,
dp[j][k] = dp[j-1][k-1]+k , 当前工作,工作总计j,连续k天的消耗为工作了j-1连续k-1天的消耗加上消耗k 。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long ll ;
using namespace std ;
const int N = 410 ;
int dp[N][N] ;
char s[N] ;
int main(){
int n,k ; scanf("%d%d",&n,&k) ;
scanf("%s",s+1) ;
memset(dp,0x3f3f3f3f,sizeof(dp)) ;
dp[0][0]=0 ;
for(int i=1 ; i<=n ; ++i){
for(int j=i ; j>=1 ; --j){
for(int k=j ; k>=1 ; --k){
dp[j][0] = min(dp[j][0],dp[j][k]) ;//当天休息,直接继承前一天的最小值
//如果为1可以选择工作
if(s[i]=='1') dp[j][k]=min(dp[j][k],dp[j-1][k-1]+k) ;
}
}
}
int ans = 0 ;
for(int i=1 ; i<=n ; ++i)
for(int j=1 ; j<=n ; ++j){
// printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
if(dp[i][j]<=k) ans=i ;
}
printf ("%d\n",ans) ;
return 0 ;
}
D- 华华和月月逛公园(tarjan求割边)*
这个讲解很详细:tarjan求割点割边
割点条件:①、该点是root点并且有两个儿子 或者
②该点不是root点,low[son]>=num[x](这里的条件是两者满足其一即可)
割边条件:low[son]>num[x]
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
typedef long long ll ;
using namespace std ;
const int N = 1e5+10 ;
const int M = 3e5+10 ;
int num[N],low[N],head[N];
int n,m,id,pos,cnt;
struct edge{
int to,next ;
bool iscut ;
}e[2*M] ;
void add(int u,int v){
e[pos].to=v , e[pos].next=head[u] ;
e[pos].iscut=true , head[u]=pos++ ;
}
void dfs(int u,int fa){
low[u]=num[u]=++id ;
// printf ("u=%d %d %d %d\n",u,head[u],e[head[u]].to,e[head[u]].next) ;
for(int i=head[u] ; i>=0 ; i=e[i].next){
int v = e[i].to ;
// printf ("u=%d v=%d fa=%d\n",u,v,fa) ;
if(v==fa) continue ;
if(!num[v]){
dfs(v,u) ;
// if(low[v]<low[u]) printf("low[%d]=%d low[%d]=%d\n",v,low[v],u,low[u]) ;
low[u] = min(low[u],low[v]) ;
if(low[v]>num[u]) ++cnt,e[i].iscut=true ;
}
else low[u]=min(low[u],num[v]) ;
}
}
int main(){
scanf("%d%d",&n,&m) ;
for(int i=1 ; i<=n ; ++i) head[i]=-1;
for(int i=0 ; i<m ; ++i){
int u,v ; scanf("%d%d",&u,&v) ;
add(u,v) , add(v,u) ;
}
for(int i=1 ; i<=n ; ++i)
if(!num[i]) dfs(i,0) ;
printf ("%d\n",m-cnt) ;
return 0 ;
}
E-数字比较(数学)
题意: 给出x,y ,问xy 和 yx哪个比较大。
题意: 两边取对数就变为lnxy ,lnyx == ylnx , xlny 然后比较一下即可。
#include <cstdio>
#include <cmath>
using namespace std ;
int main(){
long long x,y; scanf("%lld%lld",&x,&y) ;
if(x==y) printf("=\n") ;
else{
if((double)y*log(x)>(double)x*log(y)) printf(">\n") ;
else if((double)y*log(x)<(double)x*log(y)) printf("<\n") ;
else printf("=\n") ;
}
return 0 ;
}