团队设计天梯赛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;
}
posted @ 2020-11-17 10:17  大塞翁  阅读(522)  评论(0)    收藏  举报