“科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛

A 张老师和菜哭武的游戏
题目中有描述“当且仅当集合存在y和z,满足x等于y+z或者y-z”
所以考虑y-z,很容易想到辗转相减法,所以最小项是最大公约数
然后题目就迎刃而解了

#include 
using namespace std;
int n,a,b;
 
int gcd(int a,int b){
    if(a==b)return a;
    return a>b?gcd(a-b,b):gcd(b-a,a);
}
 
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>a>>b;
        int temp=gcd(a,b);
        int ans=n/temp;
        if(ans%2==1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

B 伤害计算
看了题解,觉得还是用atoi更加简单;
atoi主要用来将数字字符串转变为对应的整型数字,具体用法网上教程很多;
数学期望的计算公式是用各项权值乘以概率,常数的数学期望就是常数,公式推导还是挺简单的;
注意题目中小数只有.5,所以方便计算,可以将各项结果先乘以2,最后除2,看余数决定是否输出.5;
ps:字符串题目多用函数可以减少很多时间;

#include 
using namespace std;
const int MAXL=6000;
char strfuc[MAXL];
int ans;

void opera(char *str){
    char *D=strchr(str,'d');
    if(D==NULL){
        int c=atoi(str);
        ans+=2*c;
        //cout<
    }else{
        *D='\0';
        int n=atoi(str);
        int x=atoi(D+1);
        ans+=n*(x+1);
        //cout<
    }
}

int main(){
    while(cin>>strfuc){
        ans=0;
        char* str=strfuc;
        while(1){
            char* ps=strchr(str,'+');
            if(ps==NULL){
                opera(str);
                break;
            }else{
                *ps='\0';
                opera(str);
                str=ps+1;
            }
        }
        cout<<ans/2;
        if(ans%2) cout<<".5"<<endl;
        else cout<<endl;
    }
    return 0;
}

C 张老师的旅行
直觉上感觉可以用贪心去做;
结果wa了;
仔细想想还是不行,跟任务分配问题还是有区别的;
这道题dp还是比较难想的;
通俗的讲:
比如你在起点位置,你左边有i个景点,右边有j个景点,问题就是问你浏览完所有景点的最小时间;
首先你不管以什么顺序浏览,你要么最后停在i点,要么最后停在j点;
所以要分两种情况来讨论;
以最后停在i点为例;
这个问题的子问题,比如浏览完左边的i-1个景点,右边的j个景点的最短时间已经求得;
那么对于子问题两种情况的可行解(一种停在i-1点,一种停在j点),你都要回到i点,那么加上对应的路程就可以了(想象你从i-1出发走到i,或从j出发走到i);
然后问题的解就是两种情况的最小的那一个;
然后ac的代码;

#include 
using namespace std;
const int INF=0x3f3f3f;
struct point{
    int deadline;
    int coordinate;
};
point mp[1100];
point leftpointSet[1100];
point rightpointSet[1100];
int dp[1100][1100][2];

bool cmp(point a,point b){
    return a.coordinate<b.coordinate;
}

int main(){
    int n;
    cin>>n;
    int my_coordinate;
    for(int i=1;i<=n;i++){
        cin>>mp[i].coordinate;
    }
    for(int i=1;i<=n;i++){
        cin>>mp[i].deadline;
        if(mp[i].deadline==0) my_coordinate=i;
    }
    sort(mp+1,mp+1+n,cmp);
    memset(dp,INF,sizeof(dp));
    dp[0][0][1]=0;
    dp[0][0][0]=0;
    int cnt_left=0,cnt_right=0;
    for(int i=my_coordinate-1;i>0;i--){
        cnt_left++;
        leftpointSet[cnt_left].coordinate=abs(mp[i].coordinate-mp[my_coordinate].coordinate);
        leftpointSet[cnt_left].deadline=mp[i].deadline;
    }
    for(int i=my_coordinate+1;i<=n;i++){
        cnt_right++;
        rightpointSet[cnt_right].coordinate=abs(mp[i].coordinate-mp[my_coordinate].coordinate);
        rightpointSet[cnt_right].deadline=mp[i].deadline;
    }
    /*
    cout<
    for(int i=0;i<=cnt_left;i++){
        for(int j=0;j<=cnt_right;j++){
            if(i){
                dp[i][j][0]=min(dp[i-1][j][0]-leftpointSet[i-1].coordinate+leftpointSet[i].coordinate,dp[i-1][j][1]+rightpointSet[j].coordinate+leftpointSet[i].coordinate);
            }
            if(j){
                dp[i][j][1]=min(dp[i][j-1][1]-rightpointSet[j-1].coordinate+rightpointSet[j].coordinate,dp[i][j-1][0]+leftpointSet[i].coordinate+rightpointSet[j].coordinate);
            }
            if(dp[i][j][0]>leftpointSet[i].deadline && dp[i][j][1]>rightpointSet[j].deadline){
                cout<<-1<<endl;
                return 0;
            }
            if(dp[i][j][0]>leftpointSet[i].deadline) dp[i][j][0]=INF;
            if(dp[i][j][1]>rightpointSet[j].deadline) dp[i][j][1]=INF;

        }
    }
     cout<<min(dp[cnt_left][cnt_right][0],dp[cnt_left][cnt_right][1])<<endl;
     return 0;
}

D 车辆调度
注意车一次可以走到头,同时不止一辆车,也不止一个终点;
这样就要考虑车不仅会在遇到障碍物的情况下停下,也有可能在遇到车的时候停下,这时候仅仅把车的位置作为bfs时的结点就不行了;
其实这种问题,非常像一种叫华容道的传统游戏;
因为此时你上一步操作会影响下一步操作,这种情况下,一般可以将图作为bfs的结点,这样每一个节点都是起点图可能到达的一种情况,然后去找符合题目要求的图就可以了;
因为图有可能重复,可以用一个set来去掉重复的图(剪枝),同时因为k已经给了,所以如果操作步骤超过了k,就可以输出no,不需要再操作了;
ps:给出一道类似的题:Rush Hour Puzzle 有兴趣可以做一下
代码中的注释去掉,可以浏览所有结点;
ac代码:

#include 
#define P pair
using namespace std;

int h,w;
int k;

struct mp{
    char p[15][15];
    bool friend operator<(const mp &a,const mp &b){
        for(int i=1;i<=h;i++){
            for(int j=1;j<=w;j++){
                if(a.p[i][j]!=b.p[i][j]) return a.p[i][j]<b.p[i][j];
            }
        }
        return false;
    }
};

struct node{
    P car[8];
    P goal[100];
    int cnt;
    mp p;
}c;

set <mp> st;
queue <node> q;
bool ans=false;

bool move(node &res,int id,int de){
    mp &p=res.p;
    P &car=res.car[id];
    int h1,w1;
    h1=car.first;
    w1=car.second;
    if(de==1){
        int temp=w;
        if(car.second==w) return false;
        for(int i=w1+1;i<=w;i++){
            if(p.p[h1][i]=='R' || p.p[h1][i]=='X'){
                temp=i-1;
                break;
            }
        }
        p.p[car.first][car.second]='.';
        car.first=h1;
        car.second=temp;
        p.p[car.first][car.second]='R';
    }
    if(de==2){
        int temp=1;
        if(car.second==1) return false;
        for(int i=w1-1;i>=1;i--){
            if(p.p[h1][i]=='R' || p.p[h1][i]=='X'){
                temp=i+1;
                break;
            }
        }
        p.p[car.first][car.second]='.';
        car.first=h1;
        car.second=temp;
        p.p[car.first][car.second]='R';
    }
    if(de==3){
        int temp=h;
        if(car.first==h) return false;
        for(int i=h1+1;i<=h;i++){
            if(p.p[i][w1]=='R' || p.p[i][w1]=='X'){
                temp=i-1;
                break;
            }
        }
        p.p[car.first][car.second]='.';
        car.first=temp;
        car.second=w1;
        p.p[car.first][car.second]='R';
    }
    if(de==4){
        int temp=1;
        if(car.first==1) return false;
        for(int i=h1-1;i>=1;i--){
            if(p.p[i][w1]=='R' || p.p[i][w1]=='X'){
                temp=i+1;
                break;
            }
        }
        p.p[car.first][car.second]='.';
        car.first=temp;
        car.second=w1;
        p.p[car.first][car.second]='R';
    }
    /*
    cout<
    return true;
}

int main(){
    int test;
    int cnt1=0;
    int cnt2=0;
    cin>>w>>h>>k;
    for(int i=1;i<=h;i++){
        for(int j=1;j<=w;j++){
            char m;
            cin>>m;
            if(m=='R') c.car[cnt1++]={i,j};
            if(m=='D') c.goal[cnt2++]={i,j};
            c.p.p[i][j]=m;
        }
    }
    st.insert(c.p);
    q.push(c);
    while(!q.empty()){
        c=q.front();
        q.pop();
        for(int i=0;i<cnt2;i++){
            int t1=c.goal[i].first;
            int t2=c.goal[i].second;
            if(c.p.p[t1][t2]=='R'){
                ans=true;
                test=c.cnt;
                break;
            }
        }
        if(ans) break;
        if(c.cnt>=k) continue;
        for(int i=0;i<cnt1;i++){
            for(int j=1;j<=4;j++){
                node res=c;
                res.cnt++;
                if(move(res,i,j)){
                    if(st.count(res.p)) continue;
                    else st.insert(res.p),q.push(res);
                }
            }
        }
    }
    if(ans) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
    return 0;
}

E 弦
一道数学题,涉及的知识点主要有:快速幂,逆元运算和卡特兰数

#include 
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int n;
ll ans = 1;
ll powMod(ll a, ll k) {
    ll ret = 1;
    while(k) {
        if(k & 1) ret = ret * a % mod;
        a = a * a % mod; k >>= 1;
        }
    return ret;
}


int main(){
    cin >> n;
    for(int i = 1; i <= n + 1; i++) 
        ans = ans * i % mod;
    ans = powMod(2, n) * powMod(ans, mod - 2) % mod;
    cout << ans << endl;
    return 0;
}

F 排列计算
主要涉及的知识点时差分和前缀和;
问题本身并不难;
一般直观的解法,由于数据范围较大,复杂度大,所以会wa;
所以要用到差分和前缀和;
这里写关于差分的通俗比喻,便于理解,如果把每个数的编号看作一座座山,把每个数的大小比作山的海拔,那么,求第i座山的值,只要知道第i-1座山的高度,和第i座山和第i-1座山的海拔差就可以了,这里的i-1座山的高度可以通过递推求得,所以关键是记录相对海拔,比如要在编号i到编号j之间的数加k,那就可以看作,编号i到编号j之间的山的相对海拔比周围大k,那么只要使第i座山+k,第j+1座山的高度-k就完成了相对海拔的记录,ps:中间的山相对海拔没有发生变化;
ac代码

#include 
using namespace std;
typedef long long ll;
const int MAXN=1e6;
ll d[MAXN];

int main(){
    ll n,m;
    cin>>n>>m;
    fill(d,d+MAXN,0);
    for(ll i=0;i<m;i++){
        ll s,e;
        cin>>s>>e;
        d[s]++;
        d[e+1]--;
    }
    for(ll i=1;i<=n;i++){
        d[i]+=d[i-1];
    }
    sort(d+1,d+n+1);
    ll sum=0;
    for(int i=1;i<=n;i++){
       sum+=i*d[i];
    }
    cout<<sum<<endl;
    return 0;
}

H 时空栈
本题主要考察的知识点是离散化和线段树;
网上有相应的模板;
离散化指题目中给的数据范围很大,但数据数量相对来说更小,这时直接存储数据的实际值,会使计算量过大,如果我们并不关心数据的实际大小,只关心数据的相对大小,此时就可以用一个1,2,3,4,5…,n(n为数据的总量)
这样一个数列来映射所给的数据;
比如这道题,时间只要知道先后顺序就可以了,因为一个操作对应一个时间,所以没必要去重;
然后为了快速查找相应时间的栈顶元素,我们用线段树来存储时间1到n,每个时间段,栈中的元素数量的最小值(这样到每单个时间结点,最小值就是栈中元素的数量)ps:+1表示进栈 -1表示出栈;
此时要找到每个时间点栈顶的元素,只要往前推到t时间栈的元素恰好少一,那么必然t+1为进栈操作,此时的进栈的元素就是我们要求的值;

#include 
using namespace std;
const int MAXN=1e6+5;

struct node{
    int MIN;
    int tmp;
};
node st[MAXN*4];
int Time[MAXN],tmp[MAXN],val[MAXN],op[MAXN];
int real_val[MAXN];

//一个自顶向下的线段树,可参考模板;
void push_down(int xh){ //xh;序号;
    if(st[xh].tmp){
        st[xh<<1].MIN+=st[xh].tmp;
        st[xh<<1|1].MIN+=st[xh].tmp;
        st[xh<<1].tmp+=st[xh].tmp;
        st[xh<<1|1].tmp+=st[xh].tmp;
        st[xh].tmp=0;
    }
}

void update(int xh,int l,int r,int L,int R,int v){
    //找到区间,更新并返回;
    if(L<=l && r<=R){
        st[xh].MIN+=v;
        st[xh].tmp+=v;
        return;
    }
    //没找到;传值,递推;
    push_down(xh);
    int mid=(l+r)>>1;
    if(L<=mid) update(xh<<1,l,mid,L,R,v);
    if(R>mid) update(xh<<1|1,mid+1,r,L,R,v);
    //自下而上,更新结点;
    st[xh].MIN=min(st[xh<<1].MIN,st[xh<<1|1].MIN);
}

//查询单个时间点;
int query1(int xh,int l,int r,int pos){
    if(l==r && r==pos) return st[xh].MIN;
    push_down(xh);
    int mid=(l+r)>>1;
    if(pos<=mid) return query1(xh<<1,l,mid,pos);
    else return query1(xh<<1|1,mid+1,r,pos);
}

//区间[l,r]内在点pos前最后一个小于limit的坐标;
int query2(int xh,int l,int r,int pos,int limit){
    if(l==r){
        if(st[xh].MIN<limit) return l;
        return 0;
    }
    push_down(xh);
    int mid=(l+r)>>1;
    int ans=0;
    if(pos>mid && st[xh<<1|1].MIN<limit) ans=query2(xh<<1|1,mid+1,r,pos,limit);
    if(ans) return ans;
    return query2(xh<<1,l,mid,pos,limit);
}

int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>op[i]>>Time[i];
        if(op[i]==0){
            cin>>val[i];
        }
        tmp[i]=Time[i];
    }
    sort(tmp+1,tmp+1+n);
    //没必要去重,因为时间唯一;
    /*
    for(int i=1;i<=n;i++){
        cout<
    for(int i=1;i<=n;i++){
        Time[i]=lower_bound(tmp+1,tmp+1+n,Time[i])-tmp;
        real_val[Time[i]]=val[i];
    }
    //real_val为实际的入栈顺序;
    for(int i=1;i<=n;i++){
        if(op[i]==0) update(1,1,n,Time[i],n,1);
        else if(op[i]==1) update(1,1,n,Time[i],n,-1);
        else{
            int limit=query1(1,1,n,Time[i]);
            int point=query2(1,1,n,Time[i],limit);
            int ans=real_val[point+1];
            cout<<ans<<endl;
        }
    }
    return 0;
}

你可能感兴趣的:(icpc)