SMU Summer 2024 Contest Round 6

A.Many Formulas - SMUOJ

题目大意:

        给你一个只包含数字的字符串,你可以在除了开头和结尾的任意位置,添加任意个+号,求出每种式子的结果的总和。

思路:

        字符串长度最大只有10,二进制枚举每个可能的位置计算。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n,m,k;

void sovle(){
    int sum=0;
    string s;cin>>s;
    n=s.size()-1;
    for(int i=0;i<(1<<n);i++){
        int last=0;
        map<int,int>v;
        for(int j=0;j<n;j++){
            if((i>>j)&1){
                v[j]=1;
            }
        }
        for(int i=0;i<=n;i++){
            if(v[i]){
                int num=0,u=1;
                for(int j=i;j>=last;j--){
                    num+=u*(s[j]-'0');
                    u*=10;
                }
                sum+=num;
                last=i+1;
            }
        }
        int num=0,u=1;
        for(int j=n;j>=last;j--){
            num+=u*(s[j]-'0');
            u*=10;
        }sum+=num;
    }
    cout<<sum<<endl;
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    //cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

B.Tak and Cards - SMUOJ

题目大意:

        有n个数,求出选任意个数,它们的平均数为K的方案数。

思路:

        数据范围最大50,如果用三维dp,dp[i][j][k],表示前i个物品中选j个,总和为k的方案数。平均数为k,就是选x个数并且总和为x*k,那么答案就是dp[n][j][j*k],j从1到n。

反思:

        没有及时地确定dp的思路去写,因为对dp不熟练,然后用dp写的时候一开始对每一维的定义错了只开了二维,后来用三维的时候递推式子写不出来。因为我一直在考虑取max,但是这题并不是最值费用问题的dp。每个物品对于每种方案只有取或者不取,如果取,那么他就会在某些方案中有贡献(这些方案并不一定直接对答案产生贡献,有可能会在后来的某个时候产生贡献也可能一直没有贡献,所以不能舍弃),如果不取,说明该物品无法存在于某些方案(这些方案的容量不足放在这个物品或者不取的方案也可以直接对答案产生贡献)。中间状态的价值需要考虑。赋初值也是比较难的问题,好像一般是从某些维度为0的状态考虑。

代码: 

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n,m,k;
map<int,int>v;
int xx[55],a[55];
int dp[52][52][2505];

void sovle(){
    int sum=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++) {
        cin>>a[i];
    }
    for(int i=0;i<=n;i++){
        dp[i][0][0]=1;
    }
    int qum=0;
    for(int i=1;i<=n;i++){
        qum+=a[i];
        for(int j=0;j<=i;j++){
            for(int z=0;z<=qum;z++){
                if(j>=1&&z>=a[i])dp[i][j][z]=dp[i-1][j][z]+dp[i-1][j-1][z-a[i]];
                else dp[i][j][z]=dp[i-1][j][z];
            }
        }
    }
    for(int i=1;i<=n;i++){
        sum+=dp[n][i][i*k];
    }
    cout<<sum<<endl;
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    //cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

C.Wall - SMUOJ

题目大意:

        给你n*m个-1到9的任意整数,算出把所有不是-1和1的数变为1的花费之和,把x变为y的花费为Cx,y。

思路:

        最多只需要算8个数,相同的数不需要重复算。C矩阵最多10*10,并且剪枝之后复杂度不大于4e6,dfs暴力搜出来就可以。

        哎,这是一个多源最短路的问题,数据范围使得完全可以用Floyd,更快地写出来

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n,m,k,num=2e15;
map<int,int>v;
int c[15][15];
bool vis[15];

void dfs(int x,int cost){
    if(x==1){
        num=min(num,cost);
    }
    //if(vis[x]) return;
    for(int i=0;i<10;i++){
        if(vis[i]||i==x) continue;
        vis[i]=1;
        dfs(i,cost+c[x][i]);
        vis[i]=0;
    }
}

void sovle(){
    int sum=0;
    cin>>n>>m;
    for(int i=0;i<10;i++){
        for(int j=0;j<10;j++){
            cin>>c[i][j];
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>k;
            if(abs(k)==1) continue;
            v[k]++;
        }
    }
    for(auto ed:v){
        dfs(ed.first,0);
        sum+=ed.second*num;
        num=2e15;
        for(int i=0;i<10;i++){
            vis[i]=0;
        }
    }cout<<sum<<endl;
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    //cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

D.Coloring Edges on Tree - SMUOJ

题目大意:

        你需要对一棵树的边进行染色,要保证每个节点上的边没有相同的颜色至少需要多少种颜色 

思路:

        独立考虑每个节点,那么至少需要这个节点边数种的颜色。那么独立地考虑每个节点的情况下最多要花的颜色个数就是度数最大的。将他们合并起来考虑,最少花费颜色数是不是度数最大的节点的度数呢?对于度数小于它的节点,如果它们直接相连,它们会共用同一种颜色的边,那么它继续使用最大度节点已经用过的颜色必然足够,如果它们不直接相连,并且只隔一个点,那么有一种颜色它不能用在与中间点相连,但是它的度数小于最大度,可考虑颜色至少为1,也就是一定有颜色可以用来与中间点相连,与其他点相连就更没有影响了。

        确定了颜色的个数,怎么进行染色呢?

        以度数最大的点为起点进行bfs,每bfs到一个点就把上一个点和这个点之间的边染为上条边+1取模,用一个map记录一下边的颜色和点的颜色(防止重复染色)。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5+10;
int n,m,k,mmax,mmaxi;
map<PII,int>v;
map<int,int>vv;
PII c[N];

void bfs(vector<int>a[]){
    queue<int>q;
    q.push(mmaxi);
    int xx=mmax;
    int ys=0;
    for(int i=1;i<=n;i++) vv[i]=-2;
    vv[mmaxi]=-1;
    while(q.size()){
        int xy=q.front();q.pop();
        for(auto ed:a[xy]){
            if(vv[ed]!=-2) continue;
            q.push(ed);
            while(vv[xy]==ys) ys=(ys+1)%xx;
            vv[ed]=ys;
            v[mk(xy,ed)]=ys;
            v[mk(ed,xy)]=ys;
            ys=(ys+1)%xx;
        }
    }
}

void sovle(){
    cin>>n;
    vector<int>a[n+1];
    for(int i=1;i<=n-1;i++){
        cin>>c[i].first>>c[i].second;
        a[c[i].first].push_back(c[i].second);
        a[c[i].second].push_back(c[i].first);
    }
    for(int i=1;i<=n;i++){
        if(mmax<a[i].size()){
            mmax=a[i].size();
            mmaxi=i;
        }
    }
    bfs(a);
    cout<<a[mmaxi].size()<<endl;
    for(int i=1;i<=n-1;i++){
        cout<<v[mk(c[i].first,c[i].second)]+1<<endl;
    }
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    //cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

E.Fault-tolerant Network - SMUOJ

题目大意:

        现在有两排电脑,每排电脑相邻两个电脑都有连线,在两排间互相连线的花费是| ai-bj |,请找出最小的花费,使得无论断掉任意一根线也不会导致这两排电脑的失去连通性,任意两个电脑都能互相到达。

思路:

        不难发现,至少要连两根线才能保证满足条件,否则断掉中间的那根线就失去了连通性。并且自己手动模拟之后会发现,四个端点的电脑最容易断连,如果没有其他的线连在他们身上,那么只需要断开原本的线,无论其他线怎么连,这个电脑都会不可达。贪心地,我们想当然地想两两互联,只连最少的两个线。但是这就是最优的解?如果它们四个之间的差值都很大,那么花费就会很大。既然它们身上必须有至少一根线,切只需要有一根线,我们发现,还有其他连法,那就是把其中一根换成两根,分别连在其他两根电脑上,或者是两根换成四根,每个端点各一根。

        吐了,大模拟。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n,m,k;
int a[N],b[N];

void sovle(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    int min1=2e18,min2=2e18,min3=2e18,min4=2e18;
    for(int i=1;i<=n;i++){
        if(min1>abs(a[1]-b[i])){
            min1=abs(a[1]-b[i]);
        }
        if(min2>abs(a[n]-b[i])){
            min2=abs(a[n]-b[i]);
        }
        if(min3>abs(b[1]-a[i])){
            min3=abs(b[1]-a[i]);
        }
        if(min4>abs(b[n]-a[i])){
            min4=abs(b[n]-a[i]);
        }
    }
    int max1=min1+min2+min3+min4;
    max1=min(max1,abs(a[1]-b[1])+abs(a[n]-b[n]));
    max1=min(max1,abs(a[1]-b[n])+abs(a[n]-b[1]));
    max1=min(max1,abs(a[1]-b[1])+min4+min2); 
    max1=min(max1,min1+min3+abs(a[n]-b[n]));
    max1=min(max1,min1+min4+abs(a[n]-b[1]));    
    max1=min(max1,abs(a[1]-b[n])+min3+min2);
    cout<<max1<<endl;
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

F.Nearest Excluded Points - SMUOJ

题目大意:

        现在有n个点,请你找出每个点,在除了这n个点之外曼哈顿距离最近的点。

思路:

        bfs可以找到边权为1的最短路,但是如果对每一个点都做一个bfs搜索,2e5必然超时。我们观察到,最优的方案当然是在上下左右找,这样曼哈顿距离就是最小的1,但是有些点可能会被包住(上下左右都是被选中的点),那么我们就要多走一段距离,但是反过来思考,最优距离的点是通过一个个被选中的点走到的,总有一些点是不被包住的,并且它走到周围的被包住的点是1,那么我们能不能对最外围的点bfs呢?当然是可以的。首先我们将所有外围的点找出来,然后对这些点bfs,找到一个被包住的点,那么这个点的距离就是搜索点的距离+1,一定是最优,所以他们同解,这个过程是从最外围一圈圈往内缩的,所以就是最优解。时间复杂度大概是4*On ?

 反思:

        有时候看起来是搜索但是时间复杂度太高的题,也许可以试试反过来想,这样就可以剪掉很多枝。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n,m,k,dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
PII a[N];
map<PII,int>v;
map<PII,PII>ans;
queue<PII>q;

void bfs(){
    while(q.size()){
        auto u=q.front();q.pop();
        for(int i=0;i<4;i++){
            int xx=dx[i]+u.first;
            int yy=dy[i]+u.second;
            if(!v[mk(xx,yy)]||ans.count(mk(xx,yy))) continue;
            ans[mk(xx,yy)]=ans[mk(u.first,u.second)];
            q.push(mk(xx,yy));
        }
    }cout<<endl;
}

void sovle(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i].first>>a[i].second;
        v[mk(a[i].first,a[i].second)]=1;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<4;j++){
            int xx=dx[j]+a[i].first;
            int yy=dy[j]+a[i].second;
            if(!v[mk(xx,yy)]){
                q.push(mk(a[i].first,a[i].second));
                ans[mk(a[i].first,a[i].second)]=mk(xx,yy);
                break;
            }
        }
    }
    bfs();
    for(int i=0;i<n;i++){
        cout<<ans[mk(a[i].first,a[i].second)].first<<" ";
        cout<<ans[mk(a[i].first,a[i].second)].second<<endl;
    }
}

signed main()
{	
    ios::sync_with_stdio(false), cin.tie(0),cout.tie(0); 
    int t = 1;
    //cin>>t;
    while (t--){
        sovle();
    }

    return 0;
}

        

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值