周报3

补题1:Tokitsukaze and Password (easy)

题意:给一个只包含数字0到9,字母a,b,c,d,_.的字符串(1<=length<=9)和一个数字Y,其中a,b,c,d,_;都是未知的,且a,b,c,d直接各不相等,_无限制。问在没有前导0的前提下,a,b,c,d,_;填入数字,得出来的stoi(str)且是8的倍数,且小于Y.的填法有多少种。

做法:题目本身并不难,只需要五重循环枚举a,b,c,d,_;的0到9取值即可一下所有可能即可。虽然简单,但是赛时的时候做不出来,总是不会想到暴力枚举这个做法...重庆市赛的时候签到题也是没有意识到暴力枚举这个...以后要多留意数据范围,合适的时候暴力枚举即可。

unordered_set<int> ans;   //避免重复计算
    for(int a=0;a<=9;a++){
        for(int b=0;b<=9;b++){
            for(int c=0;c<=9;c++){
                for(int d=0;d<=9;d++){
                    if(a==b||a==c||a==d||b==c||b==d||c==d) continue;
                    for(int _=0;_<=9;_++){
                        string str0=str;
                        for(int i=0;i<n;i++){
                            if(str0[i]=='a') str0[i]=a+'0';
                            if(str0[i]=='b') str0[i]=b+'0';
                            if(str0[i]=='c') str0[i]=c+'0';
                            if(str0[i]=='d') str0[i]=d+'0';
                            if(str0[i]=='_') str0[i]=_+'0';
                        }
                        int num=stoi(str0);
                        if(str0[0]!='0'&&num%8==0&&num<=y||n==1&&str0[0]=='0') ans.insert(num);  //留意:单独一个0也是符合条件的
                    }
                }
            }
        }
    }
    cout<<ans.size()<<endl;

补题2:智乃的数字手串

题意:

做法:赛时一直没有想到关键点,倒是一直各种举例,思考。实质要发现一个点之后能反应过来。容易发现,不能操作的时候是1212... or 2121...都是偶数的。这是一个关键点,都是偶数的,而这个点可以引出:如果A拿完剩下个数是奇数,那么B一定能进行操作,B之后才有可能进行交换,让所有点不能操作。否则A拿完剩下偶数,但是还是能拿的话,B拿了又变成奇数,那么A一定能进行操作。所以主动权一直在拿完自己那个剩下个数是偶数的那方,他能不能进行操作让所有剩下的数不能操作?如果可以,那么他赢,如果他一直不可以,那么一直到最后拿完还是他赢。所以如果一开始是奇数的话,qcjj先手,那么qcjj赢。如果一开始是偶数,qcjj先手,那么zn赢。

void solve(){           //B  签到题??---
    // (先取后换)考虑不能操作的情况:1212... or 2121...
    // 都是偶数个的,只要剩下个数是奇数,总能进行选择。就算进行交换操作也是拿完之后变成偶数个才有机会交换之后全部不能取。否则一直拿到全部没有
    int n,x;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>x;
    if(n%2==0) cout<<"zn\n";
    else cout<<"qcjj\n";
}

补题3:智乃的36倍数(normal version)

题意:

做法:

void solve(){           //M  想到的只有o(n^2)的做法,不然还能怎么样判断? 这种题算是做题做少了,不够熟悉-----数字的性质!!!
    //36=4*9 --> 是36的倍数那么至少含有4和9这两个因子,其余再乘多少都不影响。而判断是4或9的倍数有特别之处:
    //一个数字是4的倍数,如果该数字末两位是4的倍数。
    //一个数字是9的倍数,如果各位相加是9的倍数--
    // 弃九法:一个整数abcd=1000a+100b+10c+d=(999a+99b+9c)+(a+b+c+d);前面部分一定是9的倍数,即如果各位a+b+c+d相加是9的倍数,那么该数就是9的倍数
    int n,sum=0,ans=0;
    cin>>n;
    string str;
    int cnt2[100][10]={0};       //i,j代表最后两位为i,并且余9为j的数字有几个。
    int cnt1[10]={0}; //单独记录只有一位的数字
    vector<int> back;
    for(int i=1;i<=n;i++) {
        cin >> str;
        if (str.size() == 1) cnt1[str[0]-'0']++;
        else{
            sum = 0;
            int num=stoi(str.substr(str.size()-2,2)); //末两位  num可能等于0
            for (int j = 0; j < str.size(); j++) sum += str[j] - '0';
            sum %= 9;  //sum=0-8
            cnt2[num][sum]++;
            cnt2[num][9]=cnt2[num][0];
        }
    }
    ans+=cnt1[3]*cnt1[6];  //个位和个位拼接
    ans+=cnt1[7]*cnt1[2];
    for(int i=1;i<=9;i++){
        for(int j=0;j<=99;j++){  //7+200
            if(j%4==0) ans+=cnt1[i]*cnt2[j][9-i];  //个位拼接在首部
            int num=j%10;
            num=num*10+i;
            if(num%4==0) ans+=cnt1[i]*cnt2[j][9-i]; //个位拼到尾部
        }
    }
    for(int i=0;i<=99;i++){  //0 4 8 12 ...
        if(i%4==0){
            for(int j=0;j<=8;j++){
                for(int k=0;k<=99;k++){
                    if(i==k&&j==0) ans+=(cnt2[i][j]-1)*(1+cnt2[i][j]-1);  //特例:36+36 72+72 or 3636+3636...
                    else ans+=cnt2[i][j]*cnt2[k][9-j];
                }
            }
        }
    }
    cout<<ans<<endl;
}

还有一种做法:

//f(45,1256)%36=451256%36=[(45%36*10000)%36+1256%36]%36
    //若结果=0,那么该拼接合法
    //36的一个特殊性质:100%36=28 , 10000%36=28 , 10000000000...%36=28.
    //故上式可变为f(45,1256)%36=[(45%36*28)%36+1256%36]
    把每个输入的数字直接x%36,之后cnt[x]++;单独一个数字的分开处理.然后从0--35枚举作为f(a,b)中的a.单独一位的a就不是a*28了

补题4:梯云纵

题意:

做法:

void solve(){           //7-9梯云纵
//    设 dp[i] 表示横 * 离月球 i 米时登陆月球的步数期望。根据题意,当横 * 离月球只有 1 米时,步数期望为 1;当横 * 离月球有 2 米时,步数期望为 1.5。
//    因此,可以得到初始状态 dp[1] = 1,dp[2] = 1.5。
//    对于横 * 离月球的每个距离 i,有两种情况:
//    横 * 可以选择上升 1 米,此时步数期望为 dp[i - 1] + 1;
//    横 * 可以选择上升 2 米,此时步数期望为 dp[i - 2] + 1。
//    因此,可以得到状态转移方程 dp[i] = 0.5 * (dp[i - 1] + 1) + 0.5 * (dp[i - 2] + 1)。
//    按照上述状态转移方程计算 dp[i],直到计算到 dp[n],即横 * 离月球有 n 米时登陆月球的步数期望。最后将结果四舍五入保留6位小数输出即可。
    cout<<fixed<<setprecision(6);
    int n,x;
    cin>>n;
    double dp[100005]={0};
    dp[1]=1,dp[2]=1.5;
    for(int i=3;i<=100000;i++) dp[i]=0.5*(dp[i-1]+1)+0.5*(dp[i-2]+1);
    while(n--){
        cin>>x;
        cout<<dp[x]<<endl;
    }
}

补题5:家庭房产

题意:

做法:不是难题。第一时间想到的是并查集,但是感觉这种题建图更好做。以后做题要多保持建图的意识,建图的话可能更加好操作实现。

typedef struct myp{
    int tag,dad,mom;
    int k;
    double departments,s;
};
typedef struct myp2{
    int tag;
    int people;
    double avedepartments;
    double aves;
};
bool cmp(myp2 a,myp2 b){
    if(a.aves!=b.aves) return a.aves>b.aves;
    return a.tag<b.tag;
}
vector<int> vct[100000];
bool vis[100000];
int exist[100000];   //标记读入时候第一列的编号,即有套房,面积信息的编号
unordered_map<int,myp> mp;
vector<myp2> ans;
void bfs(myp x){
    vis[x.tag]=1;
    queue<int> que;
    que.emplace(x.tag);
    myp2 res;
    res.tag=x.tag;
    res.people=1;
    res.avedepartments=x.departments;
    res.aves=x.s;
    while(que.size()){
        int cur=que.front();
        que.pop();
        for(auto v:vct[cur]){
            if(!vis[v]){
                vis[v]=1;
                que.emplace(v);
                res.tag=min(res.tag,v);
                res.people++;
                if(exist[v]) res.avedepartments+=mp[v].departments;
                if(exist[v]) res.aves+=mp[v].s;
            }
        }
    }
    res.avedepartments/=res.people;
    res.aves/=res.people;
    ans.emplace_back(res);
}
void solve(){                          //7-10家庭房产--建图 or 并查集  感觉建图好做一点,并查集不是很熟练
    cout<<fixed<<setprecision(3);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        myp x;
        cin>>x.tag>>x.dad>>x.mom>>x.k;
        exist[x.tag]=1;
        if(x.dad!=-1&&x.mom!=-1){   //不可缺
            vct[x.dad].emplace_back(x.mom);
            vct[x.mom].emplace_back(x.dad);
        }
        if(x.dad!=-1) {
            vct[x.tag].emplace_back(x.dad);
            vct[x.dad].emplace_back(x.tag);
        }
        if(x.mom!=-1){
            vct[x.tag].emplace_back(x.mom);
            vct[x.mom].emplace_back(x.tag);
        }
        while(x.k--){
            int kid;
            cin>>kid;
            vct[x.tag].emplace_back(kid);
            vct[kid].emplace_back(x.tag);
            if(x.dad!=-1) {   //不可缺
                vct[kid].emplace_back(x.dad);
                vct[x.dad].emplace_back(kid);
            }
            if(x.mom!=-1){
                vct[kid].emplace_back(x.mom);
                vct[x.mom].emplace_back(kid);
            }
        }
        cin>>x.departments>>x.s;
        mp[x.tag]=x;
    }
    for(auto m:mp){
        if(!vis[m.first]) bfs(m.second);
    }
    sort(ans.begin(),ans.end(),cmp);
    cout<<ans.size()<<endl;
    for(auto a:ans){
        if(a.tag<10) cout<<"000"<<a.tag<<" ";
        else if(a.tag<100) cout<<"00"<<a.tag<<" ";
        else if(a.tag<1000) cout<<"0"<<a.tag<<" ";
        else cout<<a.tag<<" ";
        cout<<a.people<<" "<<a.avedepartments<<" "<<a.aves<<endl;
    }
}

补题6:城市间紧急救援

题意:

做法:也是dijkstrad模板,但是需要记录路径,和最短路个数。稍微改一下即可。

int n,m,s,d,u,v,w;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[505];
priority_queue<pair<int,int>> pq;
int vis[505],dis[505],road[505],ans[505],arr[505],cnt[505];    //-----
//road是到达某个点的具体路径(通过终点找到起点),cnt是记录到达某个点的路径个数
void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(road,-1,sizeof(road));
    memset(dis,inf,sizeof(dis));
    //memset(cnt,(int)1,sizeof(cnt)); //不知道为什么cnt会变成一个大数字,memset慎用...
    for(int i=0;i<500;i++) cnt[i]=1;
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,w=v.second;
            //if(dis[to]==dis[from]+w&&to==d) cnt++;  //wa点--cnt计算不正确,可能中途有分叉路。只计算去到最后一个点的分岔路是少算了的。
            if(dis[to]==dis[from]+w) cnt[to]+=cnt[from];            //--------
            if(dis[to]==dis[from]+w&&ans[to]<ans[from]+arr[to]){    //--------
                road[to]=from;
                ans[to]=ans[from]+arr[to];
                //if(to==d) cnt++;   //不应该写在这个if里面的
            }
            if(dis[to]>dis[from]+w){
                dis[to]=dis[from]+w;
                pq.emplace(-dis[to],to);
                road[to]=from;                    //--------
                ans[to]=ans[from]+arr[to];         //--------
                cnt[to]=cnt[from]; //细节!!          //--------
            }
        }
    }
}
void solve(){          //7-11城市间紧急救援--
    cin>>n>>m>>s>>d;
    for(int i=0;i<n;i++) {
        cin>>arr[i];
        ans[i]=arr[i];
    }
    for(int i=1;i<=m;i++){
        cin>>u>>v>>w;
        vct[u].emplace_back(v,w);
        vct[v].emplace_back(u,w);
    }
    dijkstra(s);
    stack<int> stk;
    int cur=d;
    while(cur!=s){
        stk.emplace(cur);
        cur=road[cur];
    }
    stk.emplace(s);
    cout<<cnt[d]<<" "<<ans[d]<<endl;
    while(stk.size()){                      //-------
        cout<<stk.top();
        if(stk.top()!=d) cout<<" ";
        stk.pop();
    }
}
posted @ 2024-02-18 13:31  osir  阅读(5)  评论(0编辑  收藏  举报