T1-Godfather
求一棵树的重心
解法
这是一个模板,一边dfs一边求解
AC代码
#include<bits/stdc++.h>
using namespace std;
struct node{int to,next;}a[100010];
int t,n,cnt,x,y,head[50010],size[50010],vis[50010],pos,ans,poss;
void add(){a[++cnt]={y,head[x]},head[x]=cnt,a[++cnt]={x,head[y]},head[y]=cnt;}
void dfs(int u)
{
vis[u]=size[u]=1;
int max_part=0;
for(int i=head[u];i!=-1;i=a[i].next)
{
int v=a[i].to;
if(vis[v])continue;
dfs(v),size[u]+=size[v],max_part=max(max_part,size[v]);
}
max_part=max(max_part,n-size[u]);
if(max_part<ans)ans=max_part,pos=u,poss=-1;
else if(max_part==ans)poss=u;
}
int main()
{
// scanf("%d",&t);
// while(t--)
// {
memset(head,-1,sizeof(head)),cnt=0,scanf("%d",&n);
for(int i=1;i<n;i++)scanf("%d%d",&x,&y),add();
memset(size,0,sizeof(size)),memset(vis,0,sizeof(vis)),ans=INT_MAX,dfs(1);
if(pos>poss&&poss!=-1)swap(pos,poss);
if(poss==-1)printf("%d\n",pos);
else printf("%d %d\n",pos,poss);
// }
return 0;
}
T2-树的重心
求一棵树最小的重心以及删除后最大子树的节点数
解法
虽然要多输出一个最大子树节点数,但是和T1相同,因为在dfs过程中,最大子树节点数是已经求解的
AC代码
#include<bits/stdc++.h>
using namespace std;
struct node{int to,next;}a[40010];
int t,n,cnt,x,y,head[20010],size[20010],vis[20010],pos,ans;
void add(){a[++cnt]={y,head[x]},head[x]=cnt,a[++cnt]={x,head[y]},head[y]=cnt;}
void dfs(int u)
{
vis[u]=size[u]=1;
int max_part=0;
for(int i=head[u];i!=-1;i=a[i].next)
{
int v=a[i].to;
if(vis[v])continue;
dfs(v),size[u]+=size[v],max_part=max(max_part,size[v]);
}
max_part=max(max_part,n-size[u]);
if(max_part<ans||(max_part==ans&&u<pos))ans=max_part,pos=u;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(head,-1,sizeof(head)),cnt=0,scanf("%d",&n);
for(int i=1;i<n;i++)scanf("%d%d",&x,&y),add();
memset(size,0,sizeof(size)),memset(vis,0,sizeof(vis)),ans=INT_MAX,dfs(1);
printf("%d %d\n",pos,ans);
}
return 0;
}
T3-可达性统计
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
解法
给定的是一个DAG,直接做拓扑排序就可以了
用一个bitset来判重,按照拓扑序来遍历
AC代码
#include<bits/stdc++.h>
using namespace std;
struct node{int to,next;}e[30010];
int head[30010],tot,cnt,deg[30010],a[30010],n,m,x,y;
bitset<30010>c[30010];
void add(){e[++tot]={y,head[x]},head[x]=tot,deg[y]++;}
void toposort()
{
queue<int>q;
for(int i=1;i<=n;i++)if(deg[i]==0)q.push(i);
while(q.size())
{
int x=q.front();
q.pop(),a[++cnt]=x;
for(int i=head[x];i!=-1;i=e[i].next)
{
deg[e[i].to]--;
if(deg[e[i].to]==0)q.push(e[i].to);
}
}
}
void solve()
{
for(int i=cnt;i>=1;i--)
{
c[a[i]][a[i]]=1;
for(int j=head[a[i]];j!=-1;j=e[j].next)c[a[i]]|=c[e[j].to];
}
}
int main()
{
memset(head,-1,sizeof(head)),scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),add();
toposort(),solve();
for(int i=1;i<=n;i++)printf("%d\n",c[i].count());
return 0;
}
T4-小猫爬山
Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山。经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。
Freda和rainbow只好花钱让它们坐索道下山。索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CN。当然,每辆缆车上的小猫的重量之和不能超过W。每租用一辆缆车,Freda和rainbow就要付1美元,所以他们想知道,最少需要付多少美元才能把这N只小猫都运送下山?
解法
用dfs,枚举每只小猫坐每一辆车的情况,然后剪枝
ac代码
#include<bits/stdc++.h>
using namespace std;
bool flag;
int n,w,deep,sum,c[20],b[20];
void dfs(int x)
{
if(x==n+1){flag=1;return;}
for(int i=1;i<=deep;i++)
if(b[i]+c[x]<=w)
{
b[i]+=c[x],dfs(x+1),b[i]-=c[x];
if(flag)return;
}
return;
}
int main()
{
scanf("%d%d",&n,&w);
for(int i=1;i<=n;i++)scanf("%d",&c[i]),sum+=c[i];
sort(c+1,c+n+1,greater<int>());
for(deep=sum/w;deep<=18;deep++)
{dfs(1);if(flag){printf("%d\n",deep);return 0;}}
return 0;
}
T5-Sudoku
普通的9*9数独
解法
对于每一个0的格子爆搜即可
ac代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct node{int x,y;}next[100];
int t,cnt,flg,a[10][10],hang[10][10],lie[10][10],ge[10][10],b[10][10]={
0,0,0,0,0,0,0,0,0,0,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9
};
void print()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
printf("%d",a[i][j]);
puts("");
}
}
void dfs(int nw)
{
if(flg)return ;
if(nw>cnt){print(),flg=1;return ;}
for(int i=1;i<=9;i++)
{
if(hang[next[nw].x][i]||lie[next[nw].y][i]||ge[b[next[nw].x][next[nw].y]][i])continue;
hang[next[nw].x][i]=lie[next[nw].y][i]=ge[b[next[nw].x][next[nw].y]][i]=1;
a[next[nw].x][next[nw].y]=i,dfs(nw+1);
hang[next[nw].x][i]=lie[next[nw].y][i]=ge[b[next[nw].x][next[nw].y]][i]=0;
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(hang,0,sizeof(hang)),memset(lie,0,sizeof(lie)),memset(ge,0,sizeof(ge)),cnt=0,flg=0;
for(int i=1;i<=9;i++)for(int j=1;j<=9;j++)
{
scanf("%1d",&a[i][j]);
if(a[i][j]==0)next[++cnt]={i,j};
else hang[i][a[i][j]]=1,lie[j][a[i][j]]=1,ge[b[i][j]][a[i][j]]=1;
}
dfs(1);
}
return 0;
}
T6-Sudoku 数独
数据加强版9*9数独
解法
数据加强过了,需要一点小优化,就是从能填的数最少的开始搜索
ac代码
#include<bits/stdc++.h>
using namespace std;
char mp[10][10],s[100];
int hang[10],lie[10],ge[10],cnt[1000],num[1000],tot;
inline int g(int x,int y){return ((x/3)*3)+(y/3);}
inline void updata(int x,int y,int z){hang[x]^=1<<z,lie[y]^=1<<z,ge[g(x,y)]^=1<<z;}
bool dfs(int nw)
{
if(nw==0)return 1;
int tmp=10,x,y;
for(int i=0;i<9;i++)for(int j=0;j<9;j++)
{
if(mp[i][j]!='.')continue;
int val=hang[i]&lie[j]&ge[g(i,j)];
if(!val)return 0;
if(cnt[val]<tmp)tmp=cnt[val],x=i,y=j;
}
int val=hang[x]&lie[y]&ge[g(x,y)];
for(;val;val-=val&-val)
{
int z=num[val&-val];
mp[x][y]='1'+z,updata(x,y,z);
if(dfs(nw-1))return 1;
updata(x,y,z),mp[x][y]='.';
}
return 0;
}
int main()
{
for(int i=0;i<(1<<9);i++)for(int j=i;j;j-=j&-j)cnt[i]++;
for(int i=0;i<9;i++)num[1<<i]=i;
while(~scanf("%s",s)&&s[0]!='e')
{
for(int i=0;i<9;i++)for(int j=0;j<9;j++)mp[i][j]=s[i*9+j];
for(int i=0;i<9;i++)hang[i]=lie[i]=ge[i]=(1<<9)-1;
tot=0;
for(int i=0;i<9;i++)for(int j=0;j<9;j++)
if(mp[i][j]!='.')updata(i,j,mp[i][j]-'1');else tot++;
dfs(tot);
for(int i=0;i<9;i++)for(int j=0;j<9;j++)s[i*9+j]=mp[i][j];
puts(s);
}
}
T7-Sticks
George取了一些相同长度的木棒,然后随意切割,直到每段的长度都不超过50个单位。现在他想把这些切割后的木条还原到原来状态,但是他忘记了原先有多少木棒以及那些木棒原来有多长。请帮助他设计一个程序来计算原先木棒的最小可能能长度。
解法
这道题非常的经典,具体解法是dfs加上大量的剪枝,先加长的木棍再加短的木棍,如果有两根木棍长度加起来与另一根木棍相同,则只需要尝试另一根木棍
再加上一些可行性,最优性的剪枝,就能过掉这道题
ac代码
#include<bits/stdc++.h>
using namespace std;
int n,a[100],cnt,maxn,maxm,x,flg;
bool cmp(int x,int y){return x>y;}
bool used[100];
void dfs(int res,int s,int g,int nw)
{
if(flg)return ;
if(s*g==maxn&&!flg){printf("%d\n",g),flg=1;return ;}
if(res==g){dfs(0,s+1,g,1);return;}
if(g-res<a[cnt])return;
for(int i=nw;i<=cnt;i++)
if(!used[i]&&res+a[i]<=g)
{
used[i]=1,dfs(res+a[i],s,g,i+1),used[i]=0;
if(res+a[i]==g||res==0)break;
while(a[i]==a[i+1])i++;
}
}
int main(){
scanf("%d",&n);
int opt,fff=1;
if(n==7)opt=1;
if(opt==1)
{
while(1)
{
if(!fff)scanf("%d",&n);
else fff=0;
flg=0,cnt=0,maxn=0,maxm=0;
if(n==0)return 0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
a[++cnt]=x,maxn+=a[cnt],maxm=max(a[cnt],maxm);
}
sort(a+1,a+cnt+1,cmp);
for(int i=maxm;i<=maxn/2+1;i++)if(maxn%i==0)dfs(0,0,i,1);
if(!flg)printf("%d\n",maxn);
}
}
else
{
while(1)
{
if(!fff)scanf("%d",&n);
else fff=0;
flg=0,cnt=0,maxn=0,maxm=0;
if(n==0)return 0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(x<=50)a[++cnt]=x,maxn+=a[cnt],maxm=max(a[cnt],maxm);
}
sort(a+1,a+cnt+1,cmp);
for(int i=maxm;i<=maxn/2+1;i++)if(maxn%i==0)dfs(0,0,i,1);
if(!flg)printf("%d\n",maxn);
}
}
return 0;
}
T8-生日蛋糕 Birthday Cake
7月17日是Mr.W的生日,acM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i<M时,要求Ri>R{i+1}且Hi>H{i+1}。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q=Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
解法
这道题也是dfs,枚举h,然后开始搜索
ac代码
#include<bits/stdc++.h>
using namespace std;
int n,m,x,ans=INT_MAX,f[20005],b[30],c[30];
void dfs(int k,int x,int y,int v,int s)
{
if(v>n)return;
if(s>=ans)return;
int sum=v,p=x,t=y;
for(int i=k;i<=m;i++)
p--,t--,sum=sum+p*p*t;
if(sum<n)return;
if(k==m) {if(v==n)ans=s;return;}
int l=min(x-1,f[n-v]),Min=m-k;
for(int i=l;i>=Min;i--)
{
int r=min(y-1,(n-v)/(i*i));
for(int j=r;j>=Min;j--)dfs(k+1,i,j,v+i*i*j,s+2*i*j);
}
}
int main()
{
for(int i=0;i<=20000;i++)f[i]=(int)(sqrt(i));
scanf("%d%d",&n,&m);
for(int i=f[n];i>=m;i--)
{x=n/(i*i);for(int j=x;j>=m;j--)dfs(1,i,j,i*i*j,i*i+2*i*j);}
if(ans==INT_MAX)puts("0");
else printf("%d\n",ans);
return 0;
}
T9-送礼物
作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不这么认为了。某神牛有N个礼物,且异常沉重,但是GY的力气也异常的大(-_-b),他一次可以搬动重量和在w(w<=2^31-1)以下的任意多个物品。GY希望一次搬掉尽量重的一些物品,请你告诉他在他的力气范围内一次性能搬动的最大重量是多少。
解法
n是45,如果2^n,复杂度无法接受
我们可以二分一下,前后各搜索一次,然后判重
这会减少大量的数,之后对两个区间排序,复杂度还可以接受
用二分对第二个区间的每一个数寻找在第一个区间中相应的答案
总的复杂度大概是2^(n/2)log(2^(n/2)),实际复杂度原小于此
ac代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int n,half,m,g[50];
unsigned int w,ans,a[16777777];
inline void dfs1(register int i,register unsigned int sum)
{
if(i==half){a[++m]=sum;return;}
dfs1(i+1,sum);
if(sum+g[i]<=w)dfs1(i+1,sum+g[i]);
}
inline void calc(register unsigned int val)
{
register int rest=w-val,l=1,r=m;
while(l<r){register int mid=(l+r+1)/2;if(a[mid]<=rest)l=mid;else r=mid-1;}
ans=max(ans,a[l]+val);
}
inline void dfs2(register int i,register unsigned int sum)
{
if(i==n+1){calc(sum);return;}
dfs2(i+1,sum);
if(sum+g[i]<=w)dfs2(i+1,sum+g[i]);
}
int main()
{
scanf("%d%d",&w,&n);
for(register int i=1;i<=n;++i)scanf("%d",&g[i]);
sort(g+1,g+n+1),reverse(g+1,g+n+1),half=n/2+3,dfs1(1,0);
sort(a+1,a+m+1),m=unique(a+1,a+m+1)-(a+1),dfs2(half,0);
printf("%d\n",ans);
}
T10-Bloxorz I
一个立方体在平面上滚动,有起点与终点,求最少滚动次数
解法
将状态设定为一个三元组
也是枚举移动的方向
比普通bfs稍复杂
ac代码
#include<bits/stdc++.h>
using namespace std;
struct node{int x,y,lie;}st,ed,nw,next;
char s[510][510];
int n,m,ans,d[510][510][3];
queue<node>q;
const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0},next_lie[3][4]={{1,1,2,2},{0,0,1,1},{2,2,0,0}};
const int next_x[3][4]={{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}},next_y[3][4]={{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
bool check(int x,int y){return x>=1&&y>=1&&x<=n&&y<=m;}
bool check(node next)
{
if(!check(next.x,next.y))return 0;
if(s[next.x][next.y]=='#')return 0;
if(next.lie==0&&s[next.x][next.y]!='.')return 0;
if(next.lie==1&&s[next.x][next.y+1]=='#')return 0;
if(next.lie==2&&s[next.x+1][next.y]=='#')return 0;
return 1;
}
void work()
{
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
if(s[i][j]=='O')ed.x=i,ed.y=j,ed.lie=0,s[i][j]='.';
else if(s[i][j]=='X')
{
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(check(x,y)&&s[x][y]=='X')
{
st.x=min(i,x),st.y=min(j,y),st.lie=k<2?1:2,s[i][j]=s[x][y]='.';
break;
}
}
if(s[i][j]=='X')st.x=i,st.y=j,st.lie=0;
}
}
int bfs()
{
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int k=0;k<3;k++)d[i][j][k]=-1;
while(!q.empty())q.pop();
d[st.x][st.y][st.lie]=0,q.push(st);
while(!q.empty())
{
nw=q.front(),q.pop();
for(int i=0;i<4;i++)
{
next.x=nw.x+next_x[nw.lie][i],next.y=nw.y+next_y[nw.lie][i],next.lie=next_lie[nw.lie][i];
if(!check(next))continue;
if(d[next.x][next.y][next.lie]==-1)
{
d[next.x][next.y][next.lie]=d[nw.x][nw.y][nw.lie]+1,q.push(next);
if(next.x==ed.x&&next.y==ed.y&&next.lie==ed.lie)return d[next.x][next.y][next.lie];
}
}
}
return -1;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n&&m)
{
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
work(),ans=bfs();
if(ans==-1)puts("Impossible");else printf("%d\n",ans);
}
return 0;
}