2017 Fzu校赛 [Cloned]

A - Salty Fish

海边躺着一排咸鱼,一些有梦想的咸鱼成功翻身(然而没有什么卵用),一些则是继续当咸鱼。一个善良的渔夫想要帮这些咸鱼翻身,但是渔夫比较懒,所以只会从某只咸鱼开始,往一个方向,一只只咸鱼翻过去,翻转若干只后就转身离去,深藏功与名。更准确地说,渔夫会选择一个区间[L,R],改变区间内所有咸鱼的状态,至少翻转一只咸鱼。

渔夫离开后想知道如果他采取最优策略,最多有多少只咸鱼成功翻身,但是咸鱼大概有十万条,所以这个问题就交给你了!

Input

 

包含多组测试数据。

每组测试数据的第一行为正整数n,表示咸鱼的数量。

第二行为长n的01串,0表示没有翻身,1表示成功翻身。

 

n≤100000

Output

在渔夫的操作后,成功翻身咸鱼(即1)的最大数量。

Sample Input
5
1 0 0 1 0
3
0 1 0
Sample Output
4
2
Hint

对于第一个样例,翻转区间[2,3],序列变为1 1 1 1 0。

对于第二个样例,翻转整个区间,序列变为1 0 1。

#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 100010
int sum,n,a[maxn];
int main(){
    while(scanf("%d",&n)!=EOF){
        sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum==n){
            printf("%d\n",n-1);
            continue;
        }
        int now=0,mx=0;
        for(int i=1;i<=n;i++){
            if(a[i]==1){
                now--;
                if(now<0)now=0;
            }
            if(a[i]==0){
                now++;
                mx=max(mx,now);
            }
        }
        printf("%d\n",mx+sum);
    }
}
AC代码

B - 英语考试

在过三个礼拜,YellowStar有一场专业英语考试,因此它必须着手开始复习。

这天,YellowStar准备了n个需要背的单词,每个单词的长度均为m。

YellowSatr准备采用联想记忆法来背诵这n个单词:

1、如果YellowStar凭空背下一个新词T,需要消耗单词长度m的精力

2、如果YellowSatr之前已经背诵了一些单词,它可以选择其中一个单词Si,然后通过联想记忆的方法去背诵新词T,需要消耗的精力为hamming(Si, T) * w。

hamming(Si, T)指的是字符串Si与T的汉明距离,它表示两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。

由于YellowStar还有大量繁重的行政工作,因此它想消耗最少的精力背诵下这n个单词,请问它最少需要消耗多少精力。

Input

 

包含多组测试数据。

第一行为n, m, w。

接下来n个字符串,每个字符串长度为m,每个单词均为小写字母'a'-'z'组成。

 

1≤n≤1000

1≤m, w≤10

Output

输出一个值表示答案。

Sample Input

3 4 2
abch
abcd
efgh

Sample Output

10

Hint

最优方案是:先凭空记下abcd和efgh消耗精力8,在通过abcd联想记忆去背诵abch,汉明距离为1,消耗为1 * w = 2,总消耗为10。

/*
    不要看到字符串就以为要dp!! 
    这道题关键在于把所有的单词都背过,而且只需要背一遍
    所以我们把每个单词当成一个节点跑最小生成树 
    由于两单词之间的背诵情况会有所影响,所以更新节点连边的权值可以用m或hamming(Si,T)*w 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1010
int n,m,w,num,dis[maxn][maxn],fa[maxn],ans;
struct node{
    int from,to,v;
    bool operator < (node x)const{
        return v<x.v;
    }
}e[maxn*maxn];
char ch[maxn][15];
void Insert(int from,int to,int v){
    e[++num].to=to;
    e[num].from=from;
    e[num].v=v;
}
int find(int x){
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
bool connect(int x,int y){
    int f1=find(x),f2=find(y);
    if(f1==f2)return 0;
    fa[f1]=f2;
    return 1;
}
int main(){
    freopen("Cola.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&w)!=EOF){
        ans=0;
        memset(e,0,sizeof(e));
        num=0;
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=n;i++){
            scanf("%s",ch[i]+1);
            for(int j=1;j<i;j++){
                int com=0;
                for(int k=1;k<=m;k++)
                    if(ch[i][k]==ch[j][k])com++;
                dis[i][j]=dis[j][i]=min(m,(m-com)*w);
                Insert(i,j,dis[i][j]);
            }
        }
        sort(e+1,e+num+1);
        int cnt=0;
        for(int i=1;i<=num;i++){
            if(connect(e[i].from,e[i].to)){
                ans+=e[i].v;
                cnt++;
            }
            if(cnt==n-1)break;
        }
        printf("%d\n",ans+m);
    }
}
AC代码 最小生成树

 

C - 过河

 

遥远的YS星球上,生活着两种物种名为Yellow和Star,简称物种Y和物种S。

神奇的是物种Y的体重始终为50kg,物种S的体重始终为100kg。

这天,n只生物结伴出行,在路途中,它们被一条长长的河流挡住了去路。所幸岸边停靠着一条船,船上写着:负重m kg。

YS星球上的物种有很强的时间观念,它们需要找出一种最快的方案过河:

1、要开船每次至少要有一只生物在船上

2、载着的生物总重量不能超过船的负重m

3、无论载多少只生物,船每次从岸边到另一岸边需要的时间相同,并且不考虑中间换乘时间(换句话说,要求的是最少的开船次数)

请你帮助这些Yellow、Star们,找出最少的开船次数,并且求出最少次数下的有多少种不同的方案数。

当存在某轮开船时,两个方案乘客的集合不同,认为这两个方案是不同的。

Input

包含多组测试数据。

第一行为n, m。

接下来n个数字,数字要么是50,要么是100。

1≤n≤50

200≤m≤1000

Output

输出两行,第一行为最少开船次数,第二行为不同方案数,由于方案数可能很大,对1000000007(10^9+7)取模后输出。

Sample Input
3 100
50 50 100
Sample Output
5
2
Hint

最优的两种情况为:

1和2过去,1回来,3过去,2回来,1和2过去。

1和2过去,2回来,3过去,1回来,1和2过去。

/*
    奇数时间我们是从河这边过到那边,人数是减少的,偶数时间我们是从河那边过到河这边,人数是增加的。。
    
    所以我们直接Dp统计方案数的话,会存在后效性。
    那么我们再增加一个时间状态,就能取消后效性。
    设定Dp[k][i][j]表示时间为k的时候 ,50重量的人剩余i个,100重量的人剩余j个的方案数。
    
    那么状态转移分成时间奇偶两种情况即可:
    ①k%2==1  dp[k][i][j]+=dp[k][i+x][j+y]*C(i+x,x)*C(j+y,y);
    ②k%2==0 dp[k][i][j]+=dp[k][i-x][j-y]*C(conta-i+x,x)*C(contb-j+y,y);
    
    注意,我们虽然贪心最优的时候肯定是选择一个人回来,但是这里问的是方案数,所以,我们回来的时候可能还一次回来多个人。(这里我傻逼了为了得到最优 ,在Dp过程中还贪心了一波。。。。。导致最终答案有问题Wa了好多发。。。)
*/
#include<stdio.h>  
#include<string.h>  
#include<iostream>  
using namespace std;  
#define ll __int64  
const ll mod=1e9+7;  
ll dp[250][55][55];  
ll c[55][55];  
ll a[55];  
void init()  {  
    memset(c,0,sizeof(c));  
    c[0][0]=1%mod;  
    c[1][0]=1%mod;  
    c[1][1]=1%mod;  
    for(ll i=2;i<=52;i++){  
        c[i][0]=1%mod;  
        for(ll j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }  
}  
ll C(ll x,ll y){  
    if(y==0)return 1;  
    return c[x][y];  
}  
int main(){  
    init();  
    ll n,m;  
    while(~scanf("%I64d%I64d",&n,&m)){  
        ll conta=0,contb=0;  
        for(ll i=1;i<=n;i++){  
            scanf("%I64d",&a[i]);  
            if(a[i]==50)conta++;  
            else contb++;  
        }  
        memset(dp,0,sizeof(dp));  
        dp[0][conta][contb]=1;  //逆推 
        for(ll k=1;k<=130;k++){  
            if(k%2==1) 
                for(ll i=0;i<=conta;i++)  
                    for(ll j=0;j<=contb;j++)  
                        for(ll x=0;x<=conta;x++){  
                            if(x*50>m||i+x>conta)break;  
                            for(ll y=0;y<=contb;y++){  
                                if(x*50+y*100>m||j+y>contb)break;  
                                if(x+y==0)continue;  
                                dp[k][i][j]+=dp[k-1][i+x][j+y]*C(i+x,x)*C(j+y,y);  
                                dp[k][i][j]%=mod;  
                            }  
                        }  
            else{  
                for(ll i=0;i<=conta;i++)
                    for(ll j=0;j<=contb;j++)
                        for(ll x=0;x<=conta;x++){  
                            if(x*50>m||i-x<0)break;  
                            for(ll y=0;y<=contb;y++){  
                                if(x*50+y*100>m||j-y<0)break;  
                                if(x+y==0)continue;  
                                dp[k][i][j]+=dp[k-1][i-x][j-y]*C(conta-i+x,x)*C(contb-j+y,y);  
                                dp[k][i][j]%=mod;  
                            }  
                        }
            }  
        }  
        for(ll i=1;i<=130;i++){  
            if(dp[i][0][0]>0){  
                printf("%I64d\n%I64d\n",i,dp[i][0][0]);
                break;  
            }  
        }  
    }  
} 
AC代码

 

D - 迷宫

某一天,YellowStar在人生的道路上迷失了方向,迷迷糊糊之中,它误入了一座迷宫中,幸运的是它在路口处发现了一张迷宫的地图。

经过它的观察,它发现这个迷宫一共有n个房间,并且这n个房间呈现一个有根树结构,它现在所在的1号房间为根,其它每个房间都有一个上级房间,连接第i个房间和它的上级房间Pi的道路长度为Wi。

在地图的背面,记载了这个迷宫中,每个房间拥有一个时空传送门,第i个房间的传送门可以花费Di单位的时间传送到它的任意一个下级房间中(如果x是y的下级房间,并且y是z的下级房间,那么x也是z的下级房间)。

YellowStar的步行速度为1单位时间走1长度,它现在想知道从1号房间出发,到每一个房间的最少时间。

Input

包含多组测试数据。

第一行输入n表示n个房间。

第二行输出n个数字,第i个数字Di表示i号房间传送器需要花费的时间。

接下来n-1行,第i行包含两个数字Pi和Wi,表示i+1号房间的上级房间为Pi,道路长度为Wi。

1≤n≤100000

1≤Di, Wi≤10^9

Output

输出n个数,第i个数表示从1号房间出发到i号房间的最少时间。 (注意,输出最后一个数字后面也要加一个空格)

Sample Input

5
99 97 50 123 550
1 999
1 10
3 100
3 44

Sample Output

0 99 10 60 54

Hint

初始在1号房间,到1号房间的代价为0。

通过1号房间的传送门传送到2号房间,到2号房间的代价为99。

通过1号房间走到3号房间,到3号房间的代价为10。

通过1号房间走到3号房间,在通过3号房间的传送门传送到4号房间,到4号房间的代价为60。

通过1号房间走到3号房间,在通过3号房间走到5号房间,到5号房间的代价为54。

/*
    每个节点有两种更新方式,一个是由祖先直接传送过来,另一个是由父亲节点走过来 
    根据这个特点我们dfs的时候记录下两个值,每次更新,两个值取小就可以得出答案,只需要一个dfs 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 100010
int dis[maxn],num,head[maxn],d[maxn],n;
struct node{
    int to,pre,v;
}e[maxn];
void Insert(int from,int to,int v){
    e[++num].to=to;
    e[num].v=v;
    e[num].pre=head[from];
    head[from]=num;
}
void dfs(int now,int w1,int w2){
    dis[now]=min(w1,w2);
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        int w=min(w1,dis[now]+d[now]);
        dfs(to,w,w2+e[i].v);
    }
}
int main(){
    while(scanf("%d",&n)!=EOF){
        memset(dis,127/3,sizeof(dis));
        memset(e,0,sizeof(e));
        memset(head,0,sizeof(head));
        num=0;
        for(int i=1;i<=n;i++)scanf("%d",&d[i]);
        int x,y;
        for(int i=2;i<=n;i++){
            scanf("%d%d",&x,&y);
            Insert(x,i,y);
        }
        dis[1]=0;
        dfs(1,d[1],0);
        for(int i=1;i<=n;i++)printf("%d ",dis[i]);
        printf("\n");
    }
}
AC代码

 

F - 奖励

福州大学某班共有n名学生,这些学生这学期都选修了3门课,分别是大物、高数、大英。YellowStar想对这学期总绩点不低于3.0的学生进行奖励,所以他将每名学生每门课程的分数告诉你,并希望你能告诉他他必须对多少名学生进行奖励。

对于单门课程的绩点计算方法是(分数/10-5),如分数是69,那么绩点就是1.9,学生的总绩点就是他每门课程的绩点的平均值。但是要注意的是,当某门课程的分数小于60时,就会被视为挂科,总绩点直接为0。

Input

包含多组测试数据。

第一行输入一个整数n,表示一共有n名学生。

接下来输入n行,每行3个整数,分别表示当前学生的3门课程的分数,分数都不大于100且不小于0。

n≤500000

Output

输出一个整数,表示有多少人的总绩点不低于3.0。

Sample Input

3
59 50 92
60 80 97
83 94 67

Sample Output

1

Hint第一名同学有低于60分的科目,所以总绩点为0。 第二名同学的总绩点为(1+3+4.7)/3=2.9 第三名同学的总绩点为(3.3+4.4+1.7)/3约为3.13 只有最后一名同学的总绩点超过3.0

/*
    比较坑的一道题,骗我交了三遍
    对精度要求非一般的高。。。。就是只要你用double算就肯定错
    A的方法是不等式移项判断
*/
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int main(){
    while(scanf("%d",&n)!=EOF){
        int a,b,c,ans=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&a,&b,&c);
            if(a>=60&&b>=60&&c>=60&&(a+b+c)>=240)ans++;
        }
        printf("%d\n",ans);
    }
}
AC代码

 

 

I - 浪里个浪

TonyY是一个喜欢到处浪的男人,他的梦想是带着兰兰姐姐浪遍天朝的各个角落,不过在此之前,他需要做好规划。

现在他的手上有一份天朝地图,上面有n个城市,m条交通路径,每条交通路径都是单行道。他已经预先规划好了一些点作为旅游的起点和终点,他想选择其中一个起点和一个终点,并找出从起点到终点的一条路线亲身体验浪的过程。但是他时间有限,所以想选择耗时最小的,你能告诉他最小的耗时是多少吗?

Input

 

包含多组测试数据。

输入第一行包括两个整数n和m,表示有n个地点,m条可行路径。点的编号为1 - n。

接下来m行每行包括三个整数i, j, cost,表示从地点i到地点j需要耗时cost。

接下来一行第一个数为S,表示可能的起点数,之后S个数,表示可能的起点。

接下来一行第一个数为E,表示可能的终点数,之后E个数,表示可能的终点。

0<S, E≤n≤100000,0<m≤100000,0<cost≤100。

Output

输出他需要的最短耗时。

Sample Input

4 4
1 3 1
1 4 2
2 3 3
2 4 4
2 1 2
2 3 4

Sample Output

1
/*
    一次spfa即可 
    初始化时把所有可能的起点dis=0 
    然后入队 
    然后跑spfa 
    跑完枚举哪个终点dis最小 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 100010
int dis[maxn],n,m,num,head[maxn];
bool vis[maxn];
struct node{
    int to,pre,v;
}e[maxn];
void Insert(int from,int to,int v){
    e[++num].to=to;
    e[num].v=v;
    e[num].pre=head[from];
    head[from]=num;
}
queue<int>q;
void spfa(){
    while(!q.empty()){
        int now=q.front();q.pop();vis[now]=0;
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(dis[to]>dis[now]+e[i].v){
                dis[to]=dis[now]+e[i].v;
                if(!vis[to]){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
int main(){
    freopen("Cola.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(dis,127/3,sizeof(dis));
        memset(head,0,sizeof(head));
        memset(e,0,sizeof(e));num=0;
        while(!q.empty())q.pop();
        int x,y,z;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            Insert(x,y,z);
        }
        int S,T;
        scanf("%d",&S);
        
        for(int i=1;i<=S;i++){
            scanf("%d",&x);
            dis[x]=0;
            vis[x]=1;
            q.push(x);
        }
        spfa();
        scanf("%d",&T);
        int ans=0x7fffffff;
        for(int i=1;i<=T;i++){
            scanf("%d",&x);
            ans=min(ans,dis[x]);
        }
        printf("%d\n",ans);
    }
}
AC代码

 

posted @ 2017-09-11 19:46  Echo宝贝儿  阅读(334)  评论(0编辑  收藏  举报