2019年湘潭大学程序设计竞赛

https://ac.nowcoder.com/acm/contest/893#question

Problem A

题意:

题解:模拟

C++版本一

#include<bits/stdc++.h>
using namespace std;
int main(){
int flag = 0;
    int n1,n2 , p1,p2 , s1,s2;
    cin >> n1 >> p1 >> s1;
    cin >> n2 >> p2 >> s2 ;
    if(n1 > n2) flag = 1;
    else if(n2 > n1) flag = 0;
    else
    {
        if(p1 > p2) flag = 0;
        else if(p2 > p1) flag = 1;
        else
        {
            if(s1 > s2) flag = 0;
            else if(s1 < s2) flag = 1;
            else
            {
                flag = 2;
            }
 
        }
    }
    if(flag == 1) cout <<1 << endl;
    else if(flag == 2) cout << "God" << endl;
    else cout << 2 << endl;
 
}

Problem B

题意:

题解:模拟

C++版本一

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t ;
    cin >> t;
    while(t--){
        int sum = 0;
    int n ;
    cin >> n;
    while(n > 1){
        if(n % 10 == 0)
        n/=10,sum++;
        else {
            n++;
            sum ++;
        }
    }
        cout << sum << endl;
}
}

Problem C

题意:
证明a % 192 = 1
后面就是等差数列和

题解:
在这里插入图片描述

C++版本一

#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
    long long l , r;
    cin >> l >> r;
    long long len2 = (r -1)/ 192 ;
    long long len1 = (l-2) / 192;
    //if(r - 1<= 192) len2 = 0;
    long long sum2 = len2+1 + (96 + 96 * len2) * len2 ;
    //if(l - 1<= 192) len1 = 0;
     
    long long sum1 = len1+1 + (96 + 96 * len1) * len1;
if(l-1 == 0) sum1 = 0;
    cout << sum2 - sum1  << endl;
 
 
}
 
}

Problem D

题解:

贪心 题目乍一看像是NIM的模板题或者DP的模板题 但其实。。。 石子数累加和-最大一堆的石子数
因为每次合并代价都是小堆石子的数量,不妨设三堆石子a<= b <= c 最优方案,必然是a合并到c,再b合并到a+c;
如果先合并a和b,那么代价必然大于a+b 推而广之,最优方案为每次都往最大的那个堆上合并。 时间复杂度O(n)

C++版本一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
//std::vector<int> a;
int a[2000000 + 7];
int main(){
    int t;
    cin >> t;
    while(t--){
        //a.clear();
        int n ;
        cin >> n ;
        for(int i = 0; i < n ; i++ ) cin >> a[i];
        //sort(a.begin() , a.end());
        sort(a,a+n);
        ll sum = 0;
        for(int i = 0; i < n - 1 ; i++){
            sum += a[i ];
        }
        cout << sum << endl;
    }
 
}

Problem E

题解:

两点 写法简单一点是O(m),复杂一点是O(n),两种都可以过 O(m)的方法:循环维护到每个人吃西瓜时的西瓜数量的区间[L,R]
初始L=R=m 对于非肚量最大的人L每次减一(至少吃一个),R每次减去能ai 对于肚量最大的人L和R都减去a[i]
直到某个非肚量最大的人吃西瓜的时候如果L<0,得到答案为No 或者肚量最大的人吃西瓜时R<0,那么说明可以成功,得到答案为Yes
O(n)方法类似,求出肚量最大的人之前和之后的人的肚量之和即可优化。

C++版本一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int a[2000000 + 1];
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n, m ;
        cin >> n >> m;
        int sum = 0,index = 0;
        for(int i =  1; i <= n ; i++)
        {
            cin >> a[i];
            //sum += a[i];
            if(a[i] > sum) sum = a[i],index = i;
 
 
        }
        //cout << index << endl;
        bool flag = 0;
        int ans  = 0;
        //for(int i = 1; i < index ; i++) ans += a[i];
        int l  , r;
        l = r = m;
        int i = 1;
        while(1){
            if(i == index){
                if(r   <= 0){
                    puts("YES");
                    break;
                }
                r = r - a[i];
                l = l - a[i];
            }
            else {
                if(l  <= 0){
                    puts("NO");
                    break;
 
                }
                r = r - a[i];
                l--;
            }
    i++;
    if(i > n) i = 1;
        }
       // if(flag) puts("YES");
       // else puts("NO");
    }
    return 0;
}

Problem F

题解:

二分+ 前缀和 首先预处理出前缀和,pre0[i]表示[1…i]这个区间有多少0; 然后二分答案,再对答案进行O(n)的验证;
只需判断区间内0或1的个数加上m是否不小于当前二分的答案便可; 时间复杂度:O(nlogn)

C++版本一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
//std::vector<int> a;
char a[200000+1];
int s1[2000000 + 7],s0[2000000 + 7];
int n;
bool check(int x,int k){
int l = 1, r = x   ;
while(r <= n){
    if(s0[r] - s0[l-1] <= k || s1[r] - s1[l-1] <= k  )
        return 1;
    l++,r++;
}
return 0;
}
 
int main(){
    int t;
    cin >> t;
    while(t--){
        int  m ;
        cin >> n >> m;
        cin >> a + 1;
        int ans = -1,flag = 0;
        //cout << strlen(a) << endl;
       for(int i = 1; i <= n ; i ++){
        s0[i] = s0[i-1];
        s1[i] = s1[i-1];
        if(a[i] == '0') s0[i] ++;
        else s1[i] ++;
       }
       int l = 1, r = n;
       for(int i = 0; i < 100 ; i++){
           int  mid= (l+r)/2;
 
            //mid 表长度
            if(check(mid, m)){
                ans = max(mid,ans);
                l = mid  + 1;
            }
            else
            {
                //ans = max(mid,ans);
                r = mid  - 1;
            }
       }
       cout << ans << endl;
    }
 
}

Problem G

题解:

此问题即是把N个人分为两个集合,且满足以下条件。 i认为j是一个Truthman,那么i和j属于一个集合(一真都真一假都假)
i认为j是一个Fakeman,那么i和j属于不同集合(你真我假你假我真) 要求出符合条件的Truthman最多的一种合理解。
dfs即可,任意找一个没有被分配身份的人开始dfs,假设他的身份为任意一
种,由此可以推出一些人的身份(或者矛盾),因为要使Truthman最多,所以选择人数多的集合作为Truthman(1)即可。
注意预设一个人的身份可能无法推出所有人的身份(图不连通),循环做上面 的步骤即可。 复杂度O(n+m)

C++版本一 种类并查集变形

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define clr(i) memset(i,0,sizeof(i))
 
inline int read()
{
    char c=getchar();int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x;
}
 
int par[200005],ans[200005];
vector<int> v[200005];
inline int find(int x)
{
    return x==par[x]?x:par[x]=find(par[x]);
}
int main()
{
    int t=read();
    while(t--)
    {
        clr(ans);
        int n=read(),m=read();
        for(int i=1;i<=2*n;++i) par[i]=i;
        for(int i=1;i<=m;++i)
        {
            int a=read(),b=read(),c=read();
            if(c) par[find(a)]=find(b),par[find(a+n)]=find(b+n);
            else par[find(a)]=find(b+n),par[find(b)]=find(a+n);
        }
        for(int i=1;i<=2*n;++i) v[find(i)].pb(i);
        int ok = 1;
        for(int i=1;i<=n;++i)
        {
            if(ans[i]) continue;
            int x = find(i);
            int cnt1 = 0, cnt2 = 0;
            for(int j : v[x]) if(j>n) cnt2++;else cnt1++;
            if(cnt1>cnt2)
            {
                for(int j:v[x])
                {
                    if(j>n&&ans[j-n]) {ok=0;break;}
                    if(j>n) ans[j-n]=-1;
                    else ans[j]=1;
                }
            }
            else
            {
                for(int j:v[x])
                {
                    if(j>n&&ans[j-n]) {ok=0;break;}
                    if(j>n) ans[j-n]=1;
                    else ans[j]=-1;
                }
            }
            if(!ok) break;
        }
        if(!ok) printf("-1\n");
        else
        {
            for(int i=1;i<=n;++i) if(ans[i]>0) printf("1");else printf("0");
            printf("\n");
        }
        for(int i=1;i<=2*n;++i) v[i].clear();
    }
}

C++版本二 二分图+建模

#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v,w;
int ans,cnt,flag,temp,sum;
int vis[N],VIS[N];
char str;
struct node{
    int v,w;
    node(){};
    node(int _v,int _w):v(_v),w(_w){}
};
vector<node>G[N];
bool dfs(int u,int col){
    ++sum;
    cnt+=col;
    vis[u]=col;
    bool res=1;
    for(int i=0,j=G[u].size();i<j;i++){
        int v=G[u][i].v;
        int val=G[u][i].w?col:!col;
        if(vis[v]==-1){
            res&=dfs(v,val);
        }else{
            res&=val==vis[v];
        }
    }
    return res;
}
void DFS(int u){
    vis[u]=!vis[u];
    VIS[u]=1;
    for(int i=0,j=G[u].size();i<j;i++){
        int v=G[u][i].v;
        if(!VIS[v])DFS(v);
    }
}
 
int main()
{
#ifdef DEBUG
    freopen("input.in", "r", stdin);
    //freopen("output.out", "w", stdout);
#endif
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            G[i].clear();
            vis[i]=-1;
            VIS[i]=0;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            G[u].push_back({v,w});
            G[v].push_back({u,w});
        }
        ans=1;
        for(int i=1;i<=n;i++){
            if(vis[i]==-1){
                sum=cnt=0;
                ans&=dfs(i,1);
                if(cnt+cnt<sum){
                    DFS(i);
                }
 
            }
        }
 
        if(ans){
            for(int i=1;i<=n;i++){
                cout<<vis[i];
            }
            cout<<endl;
        }else{
            cout<<-1<<endl;
        }
    }
 
#ifdef DEBUG
    printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}

Problem H

题解:

可以转化为分组背包: 每一天可以选择造成1.2.3…m点生气度,在造成生气度的同时可以省去一些
上线时间。对于每一天mm即可算出造成i点生气度时最多能省去多少时间。 预处理每一天,复杂度O(nmm)
把每一天的每一种生气度当做一个物品,那么生气度就是这个物品的空间, 省去的时间就是这个物品的价值,n个组,每个组m个物品,而背包的大小
就是可以造成生气度的最大值k
做分组背包即可求得最多能省去多少时间,用原来所需要的总时间减去最大省去的时间即是答案,背包复杂度O(n
mk),总复杂度O(nmm+nm*k)

C++版本一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int a[2000000 + 1];
int dp[205],sum[205],mx[205];
char s[205][205];
int main(){
    int t ;
    cin >> t;
    while(t--){
        memset(dp,0,sizeof(dp));
        int n , m , k;
        cin >> n >> m >> k;
        for(int i = 1; i <= n ; i ++) cin >> s[i] + 1;
            for(int i = 1; i <= n; i++){
                memset(mx,0,sizeof(mx));
                for(int j = 1 ; j <= m; j++){
                    sum[j] = s[i][j] -'0' +sum[j-1];
                }
                mx[sum[m]] = m;
                for(int j = 1; j <= m; j++){
                    for(int w = j; w <= m ; w++){
                        int tot = sum[m] - sum[w] + sum[j-1];
                        mx[tot] = max(mx[tot],m-w+j-1);
                    }
                }
                for(int j = k; j >= 0; j--){
                    for(int w = 0; w <= m ; w++){
                        if(j >= w) dp[j] = max(dp[j],dp[j-w] + mx[w]);
                    }
                }
            }
             
                cout << m * n - dp[k] << endl;
    }
 
}

C++版本二
原博客

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PB push_back
#define POP pop_back
#define D double
#define endl '\n'
typedef pair<int,int> PII;
const LL INF=1e18,mod=100000000;
const int N=2e5+7;
int n,m,k;
char a[222];
int s[222][222];//第一维表示天数,第二维表示改天咕女神的小时数,也就是在第i天咕女神j小时获得的最大不在线时间
int f[222];//统计每天女神在线时长的前缀和用的滚动数组
int cnt[222];//女神每天的在线时长
int ans[222][222];//二维背包容器
int main()
{
    int t;
    cin>>t;
    while(t--){
        memset(s,0,sizeof(s));
        memset(cnt,0,sizeof(cnt));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            scanf("%s",a+1);
            for(int j=1;j<=m;j++){
                if(a[j]=='0')f[j]=f[j-1];
                else f[j]=f[j-1]+1,cnt[i]++;
            }
            s[i][cnt[i]]=m;
            for(int p=1;p<=m;p++){
                for(int q=p;q<=m;q++){
                    s[i][cnt[i]-f[q]+f[p-1]]=max(s[i][cnt[i]-f[q]+f[p-1]],m-(q-p+1));
                }
            }
        }
        int num=m*n;
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++){
            for(int j=0;j<=cnt[i];j++){
                for(int l=k;l>=j;l--){
                    ans[i][l]=max(ans[i][l],ans[i-1][l-j]+s[i][j]);
                }
            }
        }
        printf("%d\n",num-ans[n][k]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值