2024 ICPC Asia Pacific Championship 签到题

原题链接

H. Pho Restaurant

题目大意:给出 n 行只含0与1的字符串,要求调整其中每个 1 和 0 所在字符串的位置,使得每个字符串要么全为 1,要么全部为0(不能新增字符串),求最小调整次数
思路:对于每个字符串,假设其中 ‘1’ 的数量为 a,‘0’ 的数量为b,调整其中数量更少的字符一定是最优选择,即答案中一定包括min(a, b),并统计两种字符串数量差的最小值 res = abs(a - b)。一个字符串在清理掉数量少的‘异类’后,变为全部为 0 或者全部为 1 的字符串。
最后如果调整次数为 0 或者全1全0的字符串皆存在,那么直接输出答案。否则说明所有字符串都是全 0 或者全 1,那么多余的 1 或者 0 放入 res 最小值所在字符串,并将答案加上 res。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    int n,ans=0,allo=false,allz=false,res=1e9;
    cin>>n;
    for(int i=1;i<=n;i++) {
        string line;
        cin>>line;
        int a=count(line.begin(),line.end(),'1');
        int b=line.length()-a;
        res=min(res,abs(a-b));
        ans+=min(a,b);
        if(a>b) allo=true;
        else if(b>a) allz=true;
        else allo=allz=true;
    }

    if(ans==0||(allz&&allo)) cout<<ans<<'\n';
    else cout<<(ans+res)<<'\n';
    return 0;
}

C.Bit Counting Sequence

题目大意:给出一组数字 x x x,每个数字 x i x_i xi 代表了一个数字二进制中 1 的个数,问是否存在一段连续的数字 a a a,使得其中每个 a i a_i ai 二进制中 1 的个数等于 x i x_i xi ,如果存在,则输出第一个数 a 1 a_1 a1
思路
对于一段给定的数组 a 1 , a 2 , a 3 , . . . , a i − 1 , a i , . . . a_1,a_2,a_3,...,a_{i-1},a_i,... a1,a2,a3,...,ai1,ai,...,假设其中 a i − 1 − a i = n a_{i-1} - a_{i} = n ai1ai=n,则

n n n a i − 1 a_{i-1} ai1 a i a_{i} ai
1 1 1 x x 011 xx011 xx011 x x 100 xx100 xx100
2 2 2 x x 0111 xx0111 xx0111 x x 1000 xx1000 xx1000
3 3 3 x x 01111 xx01111 xx01111 x x 10000 xx10000 xx10000
. . . ... ... . . . ... ... . . . ... ...
m − 1 m-1 m1 x x 011...1 ( m 个 1 ) xx011...1(m个1) xx011...1(m1) x x 100...0 ( m 个 0 ) xx100...0(m个0) xx100...0(m0)

不难发现,当 a i − 1 − a i = m − 1 a_{i-1} - a_{i} = m-1 ai1ai=m1 时,符合题目要求的最小的数为

11...100...0

,即 a i a_i ai 个 1 加上 m m m 个 0,可以表示为 2 m + a i − 2 m 2^{m+a_i} - 2^m 2m+ai2m,那么第一个数即 2 m + a i − 2 m − i + 1 2^{m+a_i} - 2^m - i + 1 2m+ai2mi+1,求出第一个数之后从前向后遍历看看每个数与给定数组的二进制1个数是否符合

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(vector<ll> a,ll x)
{
    for(int i=1;i<a.size();i++) {
        if(__builtin_popcountll(x)!=a[i])
            return false;
        x++;
    }
    return true;
}
void solve()
{
    int n;
    cin>>n;
    vector<ll> a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    ll m=0,id=1;
    for(int i=2;i<=n;i++) {
        if(a[i-1]-a[i]+1>=m) {
            m=a[i-1]-a[i]+1;
            id=i;
        }
    }
    ll ans=1-id+(1ll<<m)*((1ll<<a[id])-1);
    if(ans>=0&&check(a,ans)) {
        cout<<ans<<'\n';
    } else {
        cout<<-1<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    int t;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

J.There and Back Again

题目大意:给定一个图,起点 1 终点 n,要求找出从起点走到终点并从终点返回起点的最短路径,同时从起点到终点与从终点到起点经过的边不能完全相同。
思路
从起点到终点的道路选择最短路一定是最优解之一,因为是双向道路。跑一边dijkstra并记录路径,得到从起点开始的距离数组,根据记录的路径往回走,对于路径中的每个点枚举它能走的其他所有边,寻找最小值即可。当不存在这样的‘其他边’或者起点不能到达终点时无解。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define y second
#define x first
int h[100010],e[600010],ne[600010],w[600010],idx;
int dist[100010],pre[100010];
ll br=1e18,n,m; // 回去的路的最小长度
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra()
{
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0,pre[1]=1;
    priority_queue<pii,vector<pii>,greater<>> q;
    q.push({dist[1],1});
    while(!q.empty()) {
        int u=q.top().y;
        q.pop();
        for(int i=h[u];~i;i=ne[i]) {
            int j=e[i];
            if(dist[j]>dist[u]+w[i]) {
                pre[j]=u;
                dist[j]=dist[u]+w[i];
                q.push({dist[j],j});
            }
        }
    }
}
void solve()
{
    set<int> edge;
    int p=n,last=0,Front=n;
    do {
        p=Front,Front=pre[p];
        // cout<<p<<": ";
        for(int i=h[Front];~i;i=ne[i]) {
            int j=e[i];
            if(j==p) {
                edge.insert(i);
                edge.insert(i^1);
            }
        }
        for(int i=h[p];~i;i=ne[i]) {
            int j=e[i];
            if(edge.count(i)) continue;
            // cout<<j<<' ';
            br=min(br,(ll)w[i]+dist[j]+last);
        }
        // cout<<'\n';
        last+=dist[p]-dist[Front];
    } while(pre[p]!=p);
}
int main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<=m;i++) {
        ll a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }

    dijkstra();
    solve();
    if(dist[n]==0x3f3f3f3f) cout<<-1;
    else cout<<(dist[n]+br>=1e18?-1:dist[n]+br)<<'\n';
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值