团队设计天梯赛L3题解集 (部分)
L3-001 凑零钱
题意:如果将体积和价值看成一样的就成了背包问题,输出字典序最小的方案。
思路: 输出字典序最小的方案,将输入顺序变成逆序即可。记录方案则是常规动态规划思路,记录每一次决策选的哪一个。
view code
#include <bits/stdc++.h>
using namespace std;
int n,m;
int dp[105];
int g[10005][105];
int v[10005];
bool cmp(int a,int b){
return a>b;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i];
sort(v+1,v+n+1,cmp);
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
if(v[i]>j) continue;
if(dp[j]<=dp[j-v[i]]+v[i] ){
dp[j]=dp[j-v[i]]+v[i];g[i][j]=1;
}
}
}
if(dp[m]!=m){ cout<<"No Solution";return 0; }
int i=n,j=m;
int tot=0;
while(i>0){
if(g[i][j]==1){
if(tot++==0) cout<<v[i];
else
cout<<" "<<v[i];
j-=v[i];
}
i--;
}
return 0;
}
L3-002 特殊堆栈
题意: 实现栈的pop和push操作,另外加上一个求栈内第n小的操作。
思路: 每次push或pop 便让a[i]++或a[i]--,显然a[i]代表元素i在栈内的出现次数,那么sum[i]=a[1]+....a[i]就是小于等于i小的元素个数,如果我们求得了每个这样的区间和,
可得sum[1],..sum[n]这样一个上升序列,这时要找第n小的数,只需在sum数组里找到最小的大于等于n的数的下标,这下标即为第n小的数。
每次push和pop时,都需要更新sum数组,所以使用树状数组维护sum。
view code
#include <bits/stdc++.h>
using namespace std;
int tree[100005];
int lowbit(int x){
return x&(-x);
}
void update(int i,int k){ // 单点更新
while(i<=100000){
tree[i]+=k;
i+=lowbit(i);
}
}
int getsum(int i){ //求1到i的区间和
int sum=0;
while(i>0){
sum+=tree[i];
i-=lowbit(i);
}
return sum;
}
int n;
string t;
int s[100005];
int top=0;
int mid=0;
int main()
{
cin>>n;
while(n--){
cin>>t;
if(t[1]=='u'){
int x; cin>>x;
s[++top]=x;
update(x,1);
}
else if(t[1]=='o'){
if(top==0) {cout<<"Invalid\n";continue;}
int x=s[top--];
cout<<x<<endl;
update(x,-1);
}
else{
if(top==0) {cout<<"Invalid\n";continue;}
int num=top;
if(top&1) num=(num+1)/2;
else num/=2;
int l=1,r=100000;
while(l<r){
mid=l+(r-l)/2;
if(getsum(mid)<num)l=mid+1;
else r=mid;
}
cout<<l<<endl;
}
}
return 0;
}
L3-003 社交集群
一开始想岔了以为是带权并查集,后来发现没那么麻烦。
题意:给定N个人,每个人有若干个兴趣用编号表示,若这些人中有兴趣相同的,则他们是一个圈子,求一共有几个圈子和圈子里的人数。
思路: 普通并查集,可以用a[i]来存放每个人的兴趣,任意存一个即可。遍历每个人的兴趣,找f[i],map存放f[i]的人数,最后遍历
map,放入数组里排序输出。
view code
#include <bits/stdc++.h>
using namespace std;
int f[1005];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int x,int y){
int xx=find(x),yy=find(y);
if(xx!=yy){
f[yy]=xx;
}
}
int n;
int a[1005];
map<int,int> m;
vector<int> ans;
int main()
{
cin>>n;
for(int i=1;i<=1000;i++) f[i]=i;
for(int i=1;i<=n;i++){
int k;
scanf("%d: %d",&k,&a[i]);
while(k-->1){
int x; cin>>x;
merge(a[i],x);
}
}
for(int i=1;i<=n;i++){
int x=find(a[i]);
m[x]++;
}
for(auto &it :m){
ans.push_back(it.second);
}
sort(ans.begin(),ans.end());
int len=ans.size();
cout<<len<<endl;
for(int i=len-1;i>=0;i--){
if(i==len-1) cout<<ans[i];
else cout<<" "<<ans[i];
}
return 0;
}
L3-004 肿瘤诊断
题意: 3维找连通块,给定一个T,小于T数量的块忽略不计,最后求一共有多少个连一起的块。
思路: bfs ,对每个点都bfs一下,如果这个点已经访问过,或者为0就退出。
view code
#include <bits/stdc++.h>
using namespace std;
int maze[1300][130][65];
int vis[1300][130][65];
int dx[]={0,0,1,-1,0,0};
int dy[]={1,-1,0,0,0,0};
int dz[]={0,0,0,0,1,-1};
int N,M,L,T;
struct node{
int x,y,z;
node(int xx,int yy,int zz){
x=xx; y=yy;z=zz;
}
};
queue<node> q;
int bfs(int sx,int sy,int sz){
if(vis[sx][sy][sz]||!maze[sx][sy][sz]) return 0;
int sum=0;
vis[sx][sy][sz]=1;sum++;
q.push( node(sx,sy,sz));
while(!q.empty()){
node u=q.front(); q.pop();
int x=u.x,y=u.y,z=u.z;
for(int d=0;d<6;d++){
int nx=x+dx[d],ny=y+dy[d],nz=z+dz[d];
if(nx<0||nx>=M||ny<0||ny>=N||nz<0||nz>=L) continue;
if(vis[nx][ny][nz]|| !maze[nx][ny][nz])continue;
sum++;
vis[nx][ny][nz]=1;
q.push(node(nx,ny,nz));
}
}
return sum<T? 0 :sum;
}
int main()
{
queue<int> qq;
qq.push(1);
ios::sync_with_stdio(false);
cin>>M>>N>>L>>T;
for(int z=0;z<L;z++){
for(int x=0;x<M;x++){
for(int y=0;y<N;y++){
cin>>maze[x][y][z];
}
}
}
int ans=0;
for(int z=0;z<L;z++){
for(int x=0;x<M;x++){
for(int y=0;y<N;y++){
ans+=bfs(x,y,z);
}
}
}
cout<<ans;
return 0;
}
L3-005 垃圾箱分布
思路:有点繁琐,对每个垃圾桶跑一次dijikstra即可。
view code
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
struct E{
int to,next,w;
}edge[20005];
int head[1100];
int tot=1; // 如果默认head数组里为0,tot应从1开始,不然就会很惨
void AddEdge(int u,int v,int w){ //前向星存图
edge[tot].to=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
int N,M,K,Ds;
int d[15][1105];
int vis[1105];
priority_queue <pair<int,int>> q;
char p1[10],p2[10];//这里用字符串数组端点,方便转换成数字
void dijikstra(int s){
for(int i=0;i<=1100;i++){ d[s][i]=INF;vis[i]=0; }
d[s][1000+s]=0;
q.push(make_pair(0,1000+s));
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x]){continue;} vis[x]=1;
for(int i=head[x]; i!=0; i=edge[i].next ){
int v=edge[i].to,w=edge[i].w;
if(d[s][v]>d[s][x]+w){
d[s][v]=d[s][x]+w;
q.push(make_pair(-d[s][v],v));
}
}
}
}
int main()
{
cin>>N>>M>>K>>Ds;
while(K--){
int w;
cin>>p1>>p2>>w;
int u=0,v=0;
if(p1[0]=='G'){
u=atoi(p1+1);
u+=1000;
}
else u=stoi(p1);
if(p2[0]=='G'){
v=stoi(p2+1);
v+=1000;
}
else v=stoi(p2);
AddEdge(u,v,w);
AddEdge(v,u,w);
}
for(int i=1001;i<=1000+M;i++){
dijikstra(i-1000);
}
int ansi=0;
double ave=INF;
int ansd=0;
for(int s=1;s<=M;s++){ //遍历每个垃圾桶到端点的距离
double sum=0;
int mmin=INF;
int flag=0;
for(int i=1;i<=N;i++){
sum+=1.00*d[s][i];
if(d[s][i]>Ds){flag=1;break;} //如果该垃圾桶有一条边大于Ds则,无须判断后序直接退出
mmin=min(d[s][i],mmin);
}
if(flag==1) continue;
if(ansd<mmin){
ansi=s;
ansd=mmin;
ave=1.00*sum/N;
}
else if(ansd==mmin){
if(1.00*sum/N<ave){
ave=1.00*sum/N;
ansi=s;
}
}
}
if(ansd==0){ cout<<"No Solution\n";return 0;}
printf("G%d\n",ansi);
printf("%d.0 %.1lf",ansd,ave);
return 0;
}
L3-007 天梯地图
思路: 模板题目,对时间和距离分别跑一次dijikstra。第一次对时间的dijikstra记录路程的d[0][i]和第二次记录路径的的d[0][i]数组,可当作两个数组,不可混淆。
view code
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
const int maxm=1e6+5;
struct E{
int to,w1,next,w2;
}edge[2][maxm];
int tot1=1,tot2=1;
int head[2][maxn];
int d[2][maxn],vis[maxn],fa[2][maxn];
int num[maxn];
void AddEdge(int i,int u,int v,int w1,int w2){
int tot;
tot=i==0?tot1:tot2;
edge[i][tot].to=v;
edge[i][tot].w1=w1;
edge[i][tot].w2=w2;
edge[i][tot].next=head[i][u];
head[i][u]=tot++;
if(i==0)tot1=tot;
else tot2=tot;
}
int n,m;
int st,en;
priority_queue <pair<int,int>> q;
void dijikstra(int f,int s){
if(!f)
for(int i=0;i<=n;i++){ d[0][i]=INF; vis[i]=0;}
else
for(int i=0;i<=n;i++){ d[1][i]=INF; vis[i]=0;} //分开初始化
num[s]=d[1][s]=d[0][s]=0;
q.push(make_pair(0,s));
while(!q.empty()){
int x=q.top().second; q.pop();
if(vis[x]){ continue;} vis[x]=1;
for(int i=head[f][x]; i>0;i=edge[f][i].next){
int v=edge[f][i].to,w1=edge[f][i].w1;
int w2=edge[f][i].w2;
if(!f){
if(d[f][v]>d[f][x]+w1){
d[f][v]=d[f][x]+w1;
num[v]=num[x]+1;
fa[f][v]=x;
q.push(make_pair(-d[f][v],v));
}
else if(d[f][v]==d[f][x]+w1){
if(num[v]>num[x]+1){
num[v]=num[x]+1;
fa[f][v]=x;
q.push(make_pair(-d[f][v],v));
}
}
}
else{
if(d[f][v]>d[f][x]+w2){
d[f][v]=d[f][x]+w2;
d[0][v]=d[0][x]+w1;
fa[f][v]=x;
q.push(make_pair(-d[f][v],v));
}
else if(d[f][v]==d[f][x]+w2){
if(d[0][v]>d[0][x]+w1){
d[0][v]=d[0][x]+w1;
fa[f][v]=x;
q.push(make_pair(-d[f][v],v));
}
}
}
}
}
}
vector<int> ans[2];
void print1(int f,int x){
cout<<d[f][en]<<": ";
for(int i=ans[f].size()-1;i>=0;i--){
cout<<ans[f][i];
if(i)cout<<" => ";
}
}
void found(int f,int x){
while(fa[f][x]!=-1){
ans[f].push_back(x);
x=fa[f][x];
}
ans[f].push_back(st);
}
bool judge(){
if(ans[0].size()==ans[1].size()){
for(int i=0;i<ans[0].size();i++){
if(ans[0][i]!=ans[1][i]) return false;
}
return true;
}
return false;
}
int main()
{
cin>>n>>m;
memset(fa,-1,sizeof(fa));
while(m--){
int u,v,flag,w1,w2;
cin>>u>>v>>flag>>w1>>w2;
if(flag){
AddEdge(0,u,v,w1,w2);AddEdge(1,u,v,w1,w2);
}
else{
AddEdge(0,u,v,w1,w2); AddEdge(0,v,u,w1,w2);
AddEdge(1,u,v,w1,w2); AddEdge(1,v,u,w1,w2);
}
}
cin>>st>>en;
dijikstra(1,st);dijikstra(0,st);
found(0,en); found(1,en);
if(judge()){
printf("Time = %d; ",d[1][en]);
printf("Distance = %d: ",d[0][en]);
for(int i=ans[0].size()-1;i>=0;i--){
cout<<ans[0][i];
if(i)cout<<" => ";
}
return 0;
}
printf("Time = ");
print1(1,en);
cout<<"\n";
printf("Distance = ");
print1(0,en);
return 0;
}
L3-008 喊山
思路:对每个点都bfs一下就好了
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
struct E{
int to,next;
}edge[maxm];
int head[maxn];
int vis[maxn];
int tot=1;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
queue<int> q;
int dist[maxn];
int bfs(int s){
if(head[s]==0) return 0;
memset(vis,0,sizeof(vis));
memset(dist,0,sizeof(dist));
q.push(s);
vis[s]=1;
int mmin=0x3f3f3f3f,mmax=-1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i!=0;i=edge[i].next){
int v=edge[i].to;
if(vis[v]) continue; vis[v]=1;
dist[v]=dist[x]+1;
if(dist[v]>mmax){
mmax=dist[v];
mmin=v;
}
else if(dist[v]==mmax){
mmin=min(v,mmin);
}
q.push(v);
}
}
return mmin;
}
int n,m,k;
int main()
{
cin>>n>>m>>k;
while(m--){
int u,v; cin>>u>>v;
AddEdge(u,v);AddEdge(v,u);
}
while(k--){
int x;cin>>x;
int ans=bfs(x);
cout<<ans<<"\n";
}
return 0;
}
L3-010 是否完全二叉树
思路:用一维数组递归建树,从根节点开始找到插入的位置。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[maxn];
int n;
void insert1(int x,int i){
if(a[i]==0){a[i]=x;return; }
if(x>a[i]) insert1(x,i*2);
else insert1(x,i*2+1);
}
queue<int> q;
void bfs(){
q.push(1);
while(!q.empty()){
int x=q.front(); q.pop();
if(x!=1)cout<<" ";
cout<<a[x];
if(a[2*x])q.push(2*x);
if(a[2*x+1]) q.push(2*x+1);
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) {
int x;cin>>x;insert1(x,1);
}
bfs();
cout<<"\n";
for(int i=1;i<=n;i++){
if(!a[i]){cout<<"NO"; return 0;}
}
cout<<"YES";
return 0;
}

浙公网安备 33010602011771号