安徽大学第九届大学生程序设计竞赛 网络预选赛

来源:互联网 发布:java多线程编程实战 编辑:程序博客网 时间:2024/04/29 16:44
A. 成功人士
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 250   Submission Accepted: 76
Description
学校里共有n个学生,m门课,如果某个学生在某门课中拿到了最高分(可以是并列),则这个学生是成功人士。现在给出n个学生的m门课的成绩,问有多少个成功人士。

Input
第一行,一个整数t,表示测试数据组数,(1<=t<=100)
对于每组测试数据,第一行两个整数,n和m,(1<=n,m<=50)
接下来n行,每一行有m个数字(中间不分隔),表示这个学生每门课的成绩,其中成绩为1-9的整数。

Output
每组测试数据,一个整数,表示有多少个成功人士。

Sample Input
OriginalTransformed
23 32232321123 5917281182811111

Sample Output
OriginalTransformed
23


上代码:

#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<cstdio>using namespace std;int success[100];struct stu{    int num;    int core;};bool cmp(stu a,stu b){    return a.core >b.core;}int main(){     int t,m,n,i,j;     stu students[100];     char a[100][100];     cin>>t;     while(t--){        int ans=0;        memset(success,0,sizeof(success));        cin>>n>>m;        for(i=0;i<n;i++){            getchar();            scanf("%s",a[i]);        }        for(i=0;i<m;i++){            for(j=0;j<n;j++){                students[j].core=(int)a[j][i]-'0';                students[j].num=j;            }            sort(students,students+n,cmp);//排列             for(j=0;j<n;j++){                success[students[j].num]=1;                if(students[j+1].core!=students[j].core)                break;            }//并列的全都标记         }         for(i=0;i<n;i++){            if(success[i]==1)            ans++;        }         cout<<ans<<endl;     }      }


PS(转):

题解

取出每个人每门课的成绩,排序后找出最大的几个,然后将他们的编号插入到 set 里
最后计算 set 的元素数量即可

#include <iostream>#include <cstring>#include <set>#include <algorithm>using namespace std;const int maxn = 55;char score[maxn][maxn];set<int> ans;struct Node{    int n;    int w;    Node(int a=-1,int b=-1):n(a),w(b){}    bool operator < (const Node &rhs)const{        return w > rhs.w;    }}node[maxn];int main(){    #ifdef debug    freopen("in.txt", "r", stdin);    int START = clock();    #endif    cin.tie(0);    cin.sync_with_stdio(false);        int T;    cin >> T;        while(T--){        ans.clear();        int n,m;        cin >> n >> m;        for(int i=0;i<n;i++)            cin >> score[i];        for(int i=0;i<m;i++){            for(int j=0;j<n;j++)                node[j] = Node(j,score[j][i]);            sort(node,node+n);            int t = node[0].w;            for(int j=0;j<n;j++){                if(t == node[j].w)                    ans.insert(node[j].n);                else                    break;            }        }        cout << ans.size() << endl;    }    #ifdef debug    printf("Time:%.3fs.\n", double(clock() - START) / CLOCKS_PER_SEC);    #endif    return 0;}




B. 迭代归一
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 323   Submission Accepted: 74
Description
我们把一个正整数的每一位上的数字加起来,得到新的数字,我们把这样的一次操作称为一次迭代变换。直到这个数字只有一位数。
如,9876经过一次变换成为9+8+7+6=30,经过第二次变换成为3+0=3,结束。
那么9876就经过了两次迭代变换。如今给一个正整数,问需要迭代变换多少次后会停止。

Input
一个整数t,表示测试数据组数(1<=t<=100)。
对于每组测试数据,只有一行,表示一个正整数,正整数最多有100000位。

Output
输出若干行,每行对应每组数据需要迭代变换的次数

Sample Input
OriginalTransformed
321099999999

Sample Output
OriginalTransformed
012

上代码:

#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<cstdio>using namespace std;int main(){    char a[100010];     int t,n,flag,ans;     long long int sum;     cin>>t;     while(t--){        sum=0;        ans=0;        flag=0;        scanf("%s",a);        sum+=(int)a[0]-'0';        for(int i=1;a[i]>='0' && a[i]<='9';i++){            sum+=(int)a[i]-'0';            if(flag==0){                flag=1;                ans++;             }         }                   while(sum>=10){            cout<<ans<<" "<<sum<<endl;            n=sum;            sum=0;            while(n>0){                sum+=n%10;                n/=10;             }             ans++;         }         cout<<ans<<" "<<sum<<endl;         cout<<ans<<endl;     }     return 0;}


PS(转):

#include <iostream>#include <cstring>using namespace std; const int maxn = 100005;char s[100005];int main(){    #ifdef debug    freopen("in.txt", "r", stdin);    int START = clock();    #endif    cin.tie(0);    cin.sync_with_stdio(false);         int T;     scanf("%d",&T);    while(T--){        int ans = 0;        scanf("%s",s);        int size = strlen(s);        int n = 0;        bool flag = false;        for(int i = 0;i < size;i++){            n += s[i]-'0';            if(flag)                ans=1;            if(!flag && s[i] - '0' > 0)                flag = true;        }        while(n>=10){            ans++;            int t = 0;            while(n){                t += n%10;                n /= 10;            }            n = t;        }        printf("%d\n",ans);    }     #ifdef debug    printf("Time:%.3fs.\n", double(clock() - START) / CLOCKS_PER_SEC);    #endif    return 0;}



C. 置换的魔术
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 452   Submission Accepted: 30
Description
有n个正整数,其中1到n的正整数出现且只出现一次的序列,称为1-n的一个排列。
如1,2,3和3,1,2都是1-3的排列,但是1,3,3不是1-3的排列。
如今,给n个数,问最少修改几个数,可以使得序列成为1-n的一个排列。

Input
一个整数t,表示测试数据的组数,(1<=t<=210)
对于每一组测试数据,第一行为一个整数n,(1 <= n <= 500)
第二行有n个整数a1,a2,……an,空格分隔,(ai为任意的32位有符号正整数)。

保证多组数据中的n的和不超过100000。

Output
每组测试数据,输出一个整数,表示最少修改几个数。

Sample Input
OriginalTransformed
251 3 2 4 561 1 1 1 1 1

Sample Output
OriginalTransformed
05

上代码:

/*怎么看都觉得坑,按下面解题者的意思来说就是,测试数据量和题目给的不一样,数组得开大一点,难怪我的总是runtime error,一个同学数组开到100000+10,通过了*/

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
using namespace std;
int vis[510];//这里如果开到100000+10,应该就通过了
int main(){
     int t,n,ans;
     long long int x;
     cin>>t;
     while(t--){
        ans=0;
        memset(vis,0,sizeof(vis));
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>x;
            if(x<=n && x>0) //显然,题目如果给的n值超过500,runtime error就能解释了
            vis[x]=1;
         }
         for(int i=1;i<=n;i++){
            if(!vis[i])
              ans++;
         }
        cout<<ans<<endl;
     }
     return 0;
}


ps(转):

题解

思路应该很容易,不过数据比较坑
用一个数组来记录 1-n 的数字哪个已经读入过了
没有读入的就是要交换的

这道题数据量和题目给的不一样
开大一点能解决

另外可以使用 set 来保存
只要是 1-n 的数字,直接扔到 set 里
最后 n-s.size() 即可

数字存在负数,因此读入的判断两侧都要判断

#include <cstdio>#include <iostream>#include <cstring>#include <set>using namespace std;int main(){    #ifdef debug    freopen("in.txt", "r", stdin);    int START = clock();    #endif    cin.tie(0);    cin.sync_with_stdio(false);        int T;    scanf("%d",&T);    while(T--){        set<int> s;        s.clear();        int n;        scanf("%d",&n);        for(int i=0;i<n;i++){            int t;            scanf("%d",&t);            if(t <= n && t >= 1)                s.insert(t);        }        printf("%d\n",n-s.size());    }     #ifdef debug    printf("Time:%.3fs.\n", double(clock() - START) / CLOCKS_PER_SEC);    #endif    return 0;}


D. 买买买
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 286   Submission Accepted: 56
Description
一天Alice打开了她日常玩的游戏,发现她里面还有n个游戏币,她想把这些游戏币花光。
现在可以买的一共三种道具,分别是房子(每一个价值1234567个游戏币),车子(每一个价值123456个游戏币),电脑(每一个价值1234个游戏币)。
现在她想知道,通过买这三种道具是否可以把n个游戏币全部花光。

Input
第一行,一个数字t(1<=t<=100)。代表测试数据数量。
对于每一组测试数据,一个整数n(1<=n<=1000000000),代表现在的游戏币。

Output
输出n行,每行输出"YES"或者"NO",表示她可以或者不可以把游戏币全部花光。

Sample Input
OriginalTransformed
2135925717851817

Sample Output
OriginalTransformed
YESNO

上代码:

/*这题我没写出来,因为金额太大,本想用背包的,结果开不了那么大的数组~~~~~~,看看人家的吧*/

ps(转):

题解

由于最大的值非常大,如果直接 dfs 会非常慢
而且打表的文件会非常大

而看 1234567 和 1000000000 其实差别不是很大
除一下发现最多也就能买 800 多套房子
而即使全部买成汽车也就能买 8000 多辆
乘起来的数据量只有 10e6 完全是可以遍历的
也就是说,只需要枚举房子和车的数量,然后判断剩下的钱能不能整除电脑

这样做不超时的原因是房子和车的价格都非常大,可以将许多数据“跨越”掉
如果这数都很小的话,就没有办法了

#include<bits/stdc++.h>using namespace std;int main() {int T;cin >> T;while(T--) {int n;cin >> n;int flag = 0;//int a = n/1234567;//int b = n/123456;for(int i = 0 ; i <= n/1234567 ; i ++) {for(int j = 0 ; j <= n/123456 ; j ++) {if(n >= i*1234567 + j*123456){int cur = n - i*1234567 - j*123456;if(cur % 1234 == 0){flag = 1;//goto tt;break;}}}if(flag)break;}//tt:if(flag)cout << "YES" << endl;elsecout << "NO" << endl;}return 0;}



E. FJ的旅行
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 34   Submission Accepted: 2
Judge By Case
Description
每当西瓜的朋友来西瓜家看他,西瓜总是喜欢带他们逛自己的豪宅。西瓜的豪宅有N幢楼(1<=N<=1000),用1到N的整数编号。1号楼是西瓜豪宅的大门,N号楼是西瓜的储藏室。西瓜的豪宅里总共有M条道路(1<=M<=10000)。每条道路连接两栋不同的楼房,道路的长度不超过35000。
为了最好地炫耀他的豪宅,西瓜准备从大门出发,经过一些楼房到达储藏室,再经过一些楼房回到自己的大门。
他要求他的路径越短越好,但是不能经过任意一条道路多于一次。请你计算这样的一条最短路径。西瓜保证这样的路径是存在的。

Input
第一行:N和M
第2..M+1行:每行三个整数表示一条道路(起点,终点,长度)

Output
一个整数,表示最短路径长度

Sample Input
OriginalTransformed
4 51 2 12 3 13 4 11 3 22 4 2

Sample Output
OriginalTransformed
6

上代码:

/*这题我还是没写出来,这次比较有水平的一题~~~~~~,看看人家的吧*/

PS(转):

题解

本次最难的一题
要求是对于一个无向图,在每条路只走一次的情况下,从起点到终点再返回起点的最短路
看到最短路,很容易就想到 BFS ,由于每条路有一定的权值,因此需要用 Dijkstra

很显然的思路,而且能通过样例
但是如果仔细分析样例就能发现有很大的问题存在

可以很容易看出来,去的时候最短路是 3
回来的时候最短路也是 3

但是,如果单纯只是删掉第一次最短路途径的边去跑第二次最短路,有可能就会出现问题
比如,如果第一次最短路走的是 1-2-3-4 这条路,那么第二次时压根就无法返回起点

也就是说,存在有多个最短路有重边的情况
这个问题导致了单纯的搜是不行的

图论问题,不会不如试下网络流

套用网络流的概念来分析下试试

  • 最短路 = 最小费用
  • 每个边一次 = 流量为1

看上去好像没有问题,再深入分析下
最小费用的是 最小费用最大流
它求的是在所有最大流中费用最小的

那么我们先要限定流量
由于每个边只能走一次,边的流量显然是 1
如何体现去回两次呢?
可以在建一个源点,这个源点往外的流量为 2 ,将它连在起点或终点上,然后跑它到另一个点的最小费用最大流

由于其他点的流量都是 1 ,这个节点往外的流量是 2 ,因此只要有解,最大流必然为 2
此时建的图已经和题意的图存在一些差别了
可以看作从起点走两条完全不同的路(每条路的流量为 1 )到终点(汇点流量为 2 )

建图完毕,套用模板

匡斌的模板里点的编号是 0~n-1
因此加边的时候要把 u 和 v 分别减去 1
初始化的时候记得加上汇点

输出最小的费用即可

#include <cstdio>#include <cstring>#include <iostream>#include <cmath>#include <queue>#include <set>#include <algorithm>using namespace std;const int MAXN = 1005; const int MAXM = 10005; const int INF = 0x3f3f3f3f; struct Edge {     int to,next,cap,flow,cost; }edge[MAXM]; int head[MAXN],tol; int pre[MAXN],dis[MAXN]; bool vis[MAXN]; int N;//节点总个数,节点编号从0~N-1 void init(int n){     N = n;     tol = 0;     memset(head,-1,sizeof(head)); } void addedge(int u,int v,int cap,int cost) {     edge[tol].to = v;     edge[tol].cap = cap;     edge[tol].cost = cost;     edge[tol].flow = 0;     edge[tol].next = head[u];     head[u] = tol++;     edge[tol].to = u;     edge[tol].cap = 0;     edge[tol].cost = -cost;     edge[tol].flow = 0;     edge[tol].next = head[v];     head[v] = tol++; } bool spfa(int s,int t) {     queue<int>q;     for(int i = 0;i < N;i++) {         dis[i] = INF;         vis[i] = false;         pre[i] = -1;     }     dis[s] = 0;     vis[s] = true;     q.push(s);     while(!q.empty()){         int u = q.front();         q.pop();         vis[u] = false;         for(int i = head[u]; i != -1;i = edge[i].next){             int v = edge[i].to;             if(edge[i].cap > edge[i].flow &&                dis[v] > dis[u] + edge[i].cost ){                 dis[v] = dis[u] + edge[i].cost;                 pre[v] = i;                 if(!vis[v]){                     vis[v] = true;                     q.push(v);                 }             }         }     }     if(pre[t] == -1)return false;     else return true; } //返回的是最大流,cost存的是最小费用 int minCostMaxflow(int s,int t,int &cost) {     int flow = 0;     cost = 0;     while(spfa(s,t)){         int Min = INF;         for(int i = pre[t];i != -1;i = pre[edge[i^1].to]){             if(Min > edge[i].cap - edge[i].flow)                 Min = edge[i].cap - edge[i].flow;         }         for(int i = pre[t];i != -1;i = pre[edge[i^1].to]){             edge[i].flow += Min;             edge[i^1].flow -= Min;             cost += edge[i].cost * Min;         }         flow += Min;     }     return flow; } int main(){    #ifdef debug    freopen("in.txt", "r", stdin);    int START = clock();    #endif    cin.tie(0);    cin.sync_with_stdio(false);    int n, m;    while(cin>>n>>m){    init(n+1);    for(int i=0;i<m;i++){        int u,v,cost;        cin >> u >> v >> cost;        addedge(u-1,v-1,1,cost);        addedge(v-1,u-1,1,cost);    }    addedge(n-1,n,2,0);    int ans=0;    minCostMaxflow(0,n,ans);    cout << ans << endl;    }    #ifdef debug    printf("Time:%.3fs.\n", double(clock() - START) / CLOCKS_PER_SEC);    #endif    return 0;}




F. 周末大法好
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 318   Submission Accepted: 116
Description
在火星上,每年有x天。惊奇的是,和地球上一样,火星上也是有星期的概念的,连续的5天工作日,然后连续的2天周末。他们只有周末放假。
现在你的任务是,确定火星上每年可能的最少放假的天数和最多放假的天数。

Input
第一行,一个数字n。代表测试数据数量。
接下来n行,每行一个整数x (1<=x<=1,000,000),代表火星每年有x天。

Output
输出n行,每行两个整数。代表火星上每年可能的最少放假的天数和最多放假的天数。

Sample Input
OriginalTransformed
2142

Sample Output
OriginalTransformed
4 40 2

上代码:

#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<cstdio>using namespace std;long long int n,Max,Min;int main(){     int t;     cin>>t;     while(t--){        Max=0;        Min=0;        cin>>n;        Max+=n/7*2;        Min+=n/7*2;        if(n%7==6){            Max+=2;            Min+=1;        }        else if(n%7>=2){            Max+=2;        }        else if(n%7==1){            Max+=1;        }        cout<<Min<<" "<<Max<<endl;    }     return 0;}



G. 交换大法好
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 158   Submission Accepted: 31
Description
有一天,天上掉馅饼了。不过不是直接掉馅饼,是告诉你你将要得到的馅饼的数量a。聪明的你得到了一种魔法,可以在整数a中交换任意两个相邻的数字。而这种魔法,你最多只能使用k次。你使用魔法操作a,得到的最大的结果就是你最终获得的馅饼数量。

你最多可以获得的馅饼数量是多少呢?

Input
第一行,一个数字n(1<=n<=60)。代表测试数据数量。
接下来n行,每行两个整数a和k(1<=a<=1,000,000,000; 0<=k<=100)。

Output
输出n行,每行一个整数,代表你最多使用魔法k次,可以得到的最大的数字。

Sample Input
OriginalTransformed
21990 11034 2

Sample Output
OriginalTransformed
91903104

上代码:

#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<cstdio>#include<vector>using namespace std;int main(){    int t,i,j,k,p,max;    string a;    char temp;    cin>>t;    while(t--){        cin>>a;        getchar();        cin>>k;        for(i=0;i<a.length();i++){            p=i;            max=(int)a[i];            for(j=i+1;j<a.length() && j-i<=k;j++){                if((int)a[j]>max){                    p=j;                    max=(int)a[j];                }            }            temp=a[p];            for(j=p;j>i;j--){                a[j]=a[j-1];            }            a[i]=temp;            k=k-(p-i);        }        cout<<a<<endl;    }}



H. 今年多少岁
Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 478   Submission Accepted: 112
Description
小明出生于1937年7月7日,问对于给定的日期,小明几周岁了,不足一周年的部分可以忽略。

注意:1938年7月6日,小明0周岁,1938年7月7日小明1周岁,同理1939年7月6日,小明1周岁,1939年7月7日,小明2周岁。

Input
一个整数t(1<=t<=100),表示数据组数
对于每组测试数据,三个整数,逗号隔开,分别表示给定日期的年、月、日。给定的日期保证合法, 而且一定是小明出生后的日期,且小明的岁数保证在100以内。

Output
对于每一组数据,输出一个整数,表示小明的周岁数。

Sample Input
OriginalTransformed
21938 7 61938 7 7

Sample Output
OriginalTransformed
01

上代码:

#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<cstdio>using namespace std;int a[3];int main(){     int t,ans;     cin>>t;     while(t--){        cin>>a[0]>>a[1]>>a[2];        ans=0;        if(a[0]>=1937){            ans+=a[0]-1938;            if(a[1]>7 || (a[1]==7 && a[2]>=7))            ans++;         }         cout<<ans<<endl;    }     return 0;}


总结:这次网络赛做出来五题,C题如果题目数据给的没问题,应该能过六题的,剩下E题真不会,D题呢,哎,脑子转一转,其实数据给的意思可能就是让我暴力解决的。木啊!!!看别人代码也是一种是成长,尤其像我这种自己代码写得跟狗啃的一样,更应该多看看各路大神是怎么码代码的。

介绍一个不错的博客 :http://www.biyisi.cn/archives/date/2017/03


0 0