BUCT2020春季学期ACM周赛-11

Posted leaflove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BUCT2020春季学期ACM周赛-11相关的知识,希望对你有一定的参考价值。

BUCT2020春季学期ACM周赛-11

A-冲浪游戏(cf 900)

思路

贪心,本题考虑求解最小的扣款钱数,所以容易想到本体题按扣款的钱数从大到小排序(尽可能把扣款最多的游戏放到执行队列中,以减少扣款钱数),那么在按顺序遍历每个游戏,考虑游戏的执行时间,将当前游戏放到截止时间之前的时间段的最后一个未占用时间段内,如果无法找到一个时间段,那么将此游戏放到全时间段的最后一个未占用时间段(因为放在哪都是要扣款的,所以尽可能往后放,为其他游戏腾出时间)。

Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-04-30
/@FILENAME: chonglang.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
 
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
 
const int maxn=1000;
int n,m,book[maxn];
struct game{
    int t,w;
    bool operator < (const game &x)const{
        return this->w>x.w;
    }
}arr[maxn];
 
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    FAST;
    cin>>m>>n;
    for(int i=1;i<=n;i++){
        cin>>arr[i].t;
    }
    for(int i=1;i<=n;i++){
        cin>>arr[i].w;
    }
    sort(arr+1,arr+n+1);
    for(int i=1;i<=n;i++){
        bool fl=true;
        for(int j=arr[i].t;j>=1;j--){
            if(book[j]==0){
                book[j]=1;fl=false;break;
            }
        }
        if(fl){
            m-=arr[i].w;
            for(int j=n;j>=1;j--)
                if(book[j]==0){
                    book[j]=1;break;
                }
        }
    }
    cout<<m<<endl;
    return 0;
}

B-最短Hamiton路径(cf 1400)

思路

用二进制上的数代表一个点的状态,取(1)或不取(0)。题目让求从点1到n的最短汉密顿路径,即经过每个点一次,这时的状态用二进制表示就是 ((1<<n)-1 (n个1))。用(dp[i][j])表示在状态 (i) 下,从(1)(j) 的最短汉密顿路径。

(dp[i][j])可由上一个状态(上一状态就是把 j从当前状态中去掉)(dp[i^(1<<(j-1))][k])得到,其中保证(k)是中存在的点,即 $ (i>>k) & 1 $。

表示$ i$ 的第 (k) 位是(1),即经过点 (k)。 注意是$ i>>k$ 不是 (i<<k)

则状态转移方程为:(dp[i][j]=min{dp[i^(1<<j)][k]+Map[k][j]}(k=1~n)); 其含义就是枚举到达点(j)之前的前一个点(k),取其最短。

Code
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define Inf 0x3f3f3f
#define ll long long
using namespace std;
int dp[(1<<20)+5][25];
int maze[25][25];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            scanf("%d",&maze[i][j]);
    memset(dp,Inf,sizeof(dp));
    dp[1][0]=0;
    for(int i=1;i<=(1<<n)-1;i++)
    {
        for(int j=0;j<n;j++)
        {
            if((i>>j)&1)//如果i的第j位是1,也就是如果经过点j
            {
                for(int k=0;k<n;k++)
                {
                    if((i>>k)&1)//如果i的第k位是1,也就是如果经过点k
                    {
                        dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+maze[k][j]);
                    }
                }
            }
        }
    }
    printf("%d
",dp[(1<<n)-1][n-1]);
 
    return 0;
}

C-假设电话线(cf 1600)

思路

二分加最短路,对于本题的结果,可以发现具有但单调性,所以我们可用二分来枚举结果,然后对原图进行加工,对于大于二分值的权边,置为1,否则置为0,然后用Dijkstra计算最短路判断最后到n的值是否大于k。

Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-05-03
/@FILENAME: telephonlines.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;

const int maxn=5000;
//information
int n,m,k;
//graph1
int head1[maxn],next1[maxn],v[maxn],w[maxn],cnt;
//dijk
int dist[maxn],book[maxn];

void add(int x,int y,int z){
    v[++cnt]=y;w[cnt]=z;
    next1[cnt]=head1[x];head1[x]=cnt;
    return;
}

bool Dijkstra(int bl){
    CSE(dist,INF);CSE(book,0);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    dist[1]=0;box.push(make_pair(dist[1],1));
    while(!box.empty()){
        int x=box.top().second;int d=box.top().first;box.pop();
        if(book[x]) continue;
        book[x]=1;
        for(int i=head1[x];i!=-1;i=next1[i]){
            int y=v[i];int kk=(w[i]>=bl?1:0);
            if(dist[y]>kk+d){
                dist[y]=kk+d;
                box.push(make_pair(dist[y],y));
            }
        }
    }
    return dist[n]>k;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    FAST;
    CSE(head1,-1);CSE(next1,-1);
    cin>>n>>m>>k;
    for(int i=0;i<m;i++){
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);add(y,x,z);
    }
    int l=0,r=1000001,ans=-1;
    while(l<=r){
        int mid=(l+r)/2;
        if(Dijkstra(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    if(ans>1000000) ans=-1;
    if(r<0) ans=0;
    cout<<ans<<endl;
    return 0;
}

D-农场派对(cf 1500)

思路

对于去时的最短路径直接用Dijkstra算法求解即可,对于回来时的最短路径,将所有的有向边方向反转,权值不改变,之后按去时的路径求解最短路径,将两个最短路径的值加和就是最短的来回路径,遍历所有顶点求取最短路径最大值即可。

Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-04-30
/@FILENAME: fanxiangbian.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
 
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
 
const int maxn=111111;
//graph1
int first[maxn],nxt1[maxn],u1[maxn],v1[maxn],w[maxn];
int cnt,n,m,s;
//graph2
int head[maxn],nxt2[maxn],u2[maxn],v2[maxn];
//dij
int dist1[maxn],dist2[maxn];
//邻接表
void add(int x,int y,int d){
    u1[++cnt]=x;v1[cnt]=y;
    u2[cnt]=y;v2[cnt]=x;
    w[cnt]=d;
    nxt1[cnt]=first[u1[cnt]];first[u1[cnt]]=cnt;
    nxt2[cnt]=head[u2[cnt]];head[u2[cnt]]=cnt;
    return;
}
 
void Dijkstra1(int s){
    CSE(dist1,INF);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    dist1[s]=0;box.push(make_pair(0,s));
    while(!box.empty()){
        int x=box.top().second;
        int d=box.top().first;
        box.pop();
        for(int i=first[x];i!=-1;i=nxt1[i]){
            int y=v1[i];
            if(dist1[y]>d+w[i]){
                dist1[y]=d+w[i];
                box.push(make_pair(dist1[y],y));
            }
        }
    }
    return;
}
 
void Dijkstra2(int s){
    CSE(dist2,INF);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    dist2[s]=0;box.push(make_pair(0,s));
    while(!box.empty()){
        int x=box.top().second;
        int d=box.top().first;
        box.pop();
        for(int i=head[x];i!=-1;i=nxt2[i]){
            int y=v2[i];
            if(dist2[y]>d+w[i]){
                dist2[y]=d+w[i];
                box.push(make_pair(dist2[y],y));
            }
        }
    }
    return;
}
 
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    FAST;
    CSE(first,-1);CSE(nxt1,-1);CSE(head,-1);CSE(nxt2,-1);
    cin>>n>>m>>s;
    for(int i=0;i<m;i++){
        int x,y,d;
        cin>>x>>y>>d;
        add(x,y,d);
    }
    Dijkstra1(s);
    Dijkstra2(s);
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dist1[i]+dist2[i]);
    }
    cout<<ans<<endl;
    return 0;
}

E-Roadblocks(cf 1400)

思路

Dijkstra次短路径板题,用一个数组(dist1[])记录最短路径,另一个数组(dist2[])记录次短路径,执行Dijkstra算法。

当要更新最短路时执行

  1. 更新次短路径为当前最短路径的值
  2. 更新最短路径为新的最短路径的值

当不需要更新最短路但是新的路径长度大于最短路径,但是小于次短路径时执行

  1. 更新次短路径为新的路径值
Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-04-30
/@FILENAME: cidunalu.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
 
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
 
const int maxn=211111;
//graph
int first[maxn],nxt[maxn],w[maxn],u[maxn],v[maxn];
int n,m,cnt;
//dijkstra
int dis[maxn],ans[maxn];
//邻接表
void add(int x,int y,int d){
    u[++cnt]=x;v[cnt]=y;w[cnt]=d;
    nxt[cnt]=first[u[cnt]];first[u[cnt]]=cnt;
    return;
}
 
void Dijkstra(int s){
    CSE(dis,INF);CSE(ans,INF);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    dis[s]=0;box.push(make_pair(0,s));
    while(!box.empty()){
        int x=box.top().second;
        int d=box.top().first;
        box.pop();
        for(int i=first[x];i!=-1;i=nxt[i]){
            int y=v[i];
            if(dis[y]>d+w[i]){
                ans[y]=dis[y];
                dis[y]=d+w[i];
                box.push(make_pair(dis[y],y));
            }
            else if(dis[y]==d+w[i]) continue;
            else if(ans[y]>d+w[i]&&dis[y]<d+w[i]){
                ans[y]=d+w[i];
                box.push(make_pair(ans[y],y));
            }
        }
    }
    return;
}
 
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    FAST;
    CSE(first,-1);CSE(nxt,-1);
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int x,y,d;
        cin>>x>>y>>d;
        add(x,y,d);add(y,x,d);
    }
    Dijkstra(1);
    cout<<ans[n]<<endl;
    return 0;
}

F-最短路计数(cf 1400)

思路

Dijkstra简单变体,在计算最短路时,考虑两种情况,当需要更新最短路时,将当前节点的最短路径个数等于出发节点的最短路径个数,当不需要更新最短路径,但是当前路径和最短路径的值相等时,将当前最短节点的最短路径的个数加上出发节点的最短路径的个数。

Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-04-30
/@FILENAME: counzuiduanlu.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
 
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
 
const int maxn=411111;
//geaph
int first[maxn],nxt[maxn],u[maxn],v[maxn];
int n,m,cnt;
//dij
ll dist[maxn],ct[maxn],book[maxn];
//邻接表
void add(int x,int y){
    u[++cnt]=x;v[cnt]=y;
    nxt[cnt]=first[u[cnt]];first[u[cnt]]=cnt;
    return;
}
 
void Dijkstra(int s){
    CSE(dist,INF);CSE(ct,0);CSE(book,0);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    dist[s]=0;ct[s]=1;box.push(make_pair(0,s));
    while(!box.empty()){
        int x=box.top().second;
        ll d=box.top().first;
        box.pop();
        if(book[x]) continue;
        book[x]=1;
        for(int i=first[x];i!=-1;i=nxt[i]){
            int y=v[i];
            if(dist[y]>d+1){
                dist[y]=d+1;
                ct[y]=ct[x];
                box.push(make_pair(dist[y],y));
            }
            else if(dist[y]==d+1){
                ct[y]=(ct[y]+ct[x])%100003;
            }
        }
    }
    return;
}
 
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    CSE(first,-1);CSE(nxt,-1);
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    Dijkstra(1);
    for(int i=1;i<=n;i++){
        printf("%lld
",ct[i]%100003);
    }
    return 0;
}

G-最优贸易(cf 1600)

思路

本题有点类似于“农场派对”那题,因为要求每条路径上最大权和最小权之差,所以,对于最小权我们修改Dijkstra来计算每条路径上的最小权值(这里要遍历所有路径,所以对于每个顶点我们都要加入队列进行扩展),之后我们将图中的路径方向取反,然后原点改为(n),我用新得到的图计算最大权,两遍Dijkstra之后对于所有节点取每个节点处最大权减最小权的最大值即可(注意判断是否会到达该点)。对于这题可以用SPFA代替Dijkstra,可以有一定优化。

Code
/****************************************************
/@Author: Kirito
/@TIME:   2020-05-03
/@FILENAME: businessbest.cpp
/@REMARK:    
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;

const int maxn=1111111;
//information
int n,m,city[maxn];
//graph1
int head1[maxn],next1[maxn],v1[maxn],cnt1;
//graph2
int head2[maxn],next2[maxn],v2[maxn],cnt2;
//dij
int dist1[maxn],book1[maxn],dist2[maxn],book2[maxn];
//邻接表的建立
void add1(int x,int y){
    v1[++cnt1]=y;
    next1[cnt1]=head1[x];head1[x]=cnt1;
    return;
}

void add2(int x,int y){
    v2[++cnt2]=y;
    next2[cnt2]=head2[x];head2[x]=cnt2;
    return;
}

void Dijkstra1(int s){//取最小
    memcpy(dist1,city,sizeof(city));CSE(book1,0);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    box.push(make_pair(dist1[s],s));
    while(!box.empty()){
        int minn=box.top().first;int x=box.top().second;box.pop();
        if(book1[x]) continue;
        book1[x]=1;
        for(int i=head1[x];i!=-1;i=next1[i]){
            int y=v1[i];
            if(dist1[y]>minn) dist1[y]=minn;
            box.push(make_pair(dist1[y],y));
        }
    }
    return;
}

void Dijkstra2(int s){//取最大
    memcpy(dist2,city,sizeof(city));CSE(book2,0);
    priority_queue<pii,vector<pii>,greater<pii>> box;
    box.push(make_pair(dist2[s],s));
    while(!box.empty()){
        int maxx=box.top().first;int x=box.top().second;box.pop();
        if(book2[x]) continue;
        book2[x]=1;
        for(int i=head2[x];i!=-1;i=next2[i]){
            int y=v2[i];
            if(dist2[y]<maxx) dist2[y]=maxx;
            box.push(make_pair(dist2[y],y));
        }
    }
    return;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
#endif
    FAST;
    CSE(head1,-1);CSE(head2,-1);CSE(next1,-1);CSE(next2,-1);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>city[i];
    }
    for(int i=0;i<m;i++){
        int x,y,z;
        cin>>x>>y>>z;
        add1(x,y);add2(y,x);
        if(z==2){
            add1(y,x);add2(x,y);
        }
    }
    Dijkstra1(1);
    Dijkstra2(n);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(book1[i]&&book2[i]){
            ans=max(ans,dist2[i]-dist1[i]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

以上是关于BUCT2020春季学期ACM周赛-11的主要内容,如果未能解决你的问题,请参考以下文章

2020春季学期第八周学习总结

2020春季学期第三周学习总结

2020-3-14 acm训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019 解题报告+补题报告

BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解

BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解

BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解