常州大学寒假练习题解

A.添加逗号 
题目描述  对于一个较大的整数 N(1<=N<=2,000,000,000) 比如 980364535,我们常常需要一位一位数这个数字是几位数,但是如果在这 个数字每 三位加一个逗号,它会变得更加易于朗读。 因此,这个数字加上逗号成如下的模样:980,364,535 请写一个程序帮她完成这件事情 
 
输入描述: 
一行一个整数 N 
输出描述: 
一行一个字符串表示添加完逗号的结果 
示例 1 输入 
980364535 输出 
980,364,535 备注: 
1≤n≤2,000,000,000 
题解 
题目定位:签到题  
考察内容:数字拆分,字符串处理,细节处理  
题解:直接模拟即可  

参考代码  https://paste.ubuntu.com/26522962/ 

#include <bits/stdc++.h>


using namespace std;


int main(){
    int n, i, x[100005], j, m, y, q;
    scanf("%d",&n);
        i = 0;
        m = 0;
        while(n > 0){
            x[i] = n % 10;
            n = n/10;
            i++;
        }
        y = i;
        while(i > 3){
            i = i - 3;
            m++;
        }
        for(j = y - 1;j >= y - i;--j){
            cout << x[j];
        }
        for(j = 0;j < m;++j){
            cout << ",";
            for(q = 0;q < 3;++q){
                cout << x[y - i - 1];
                i++;
            }
        }
        cout << endl;
    
    return 0;

}

解2:

#include <iostream>
#include <string>
#include <sstream>
using namespace std;
string LongToString(long l)
{
    stringstream str;
    str<<l;
    return str.str();
}
int main()
{
    long l;
    cin>>l;
    string str = LongToString(l);
    size_t len = str.length();
    for(int index =(int) len-3; index > 0; index -= 3)
      str.insert(index, ",");
    cout<<str<<endl;
    return 0;

}

B.对称(图挂了) 
题目描述  萌新 AA 喜欢对称,最近她喜欢把棋子放进她的棋盘中,这个棋盘是由 N×M 个格 子构成 的(1 <= N <= 1,000,000,000;1<=M<=1,000,000,000) 为了保证对称,AA  会以这样的方 式摆放她的棋子。她把棋子放在棋盘正中央的方格内, 如果不存在这样的方格,她就会停 止。然后她以这个方格为中心把棋盘分成四部分,然后对于每 个小棋盘进行上述的操作。 下面是一个 N=7,M=15 的例子,其中'C'表示棋子 
 
这样子,需要 21 个棋子。如果 N=M=5 的话,AA 只需要摆放一个棋子,因为分成的四 个小棋盘分别是 2×2 的大小,无法在放进去新的棋子。现在,请你帮助 AA 来计算,需 要 多少个棋子。 
 
输入描述: 
一行两个整数 N,M 
输出描述: 
一行一个整数,即需要的棋子数 
示例 1 输入 
7  15 输出 
21 示例 2 输入 
3 1 输出 
1 说明 
不一定变成 4 个部分,存在中心位置即可 备注: 
1≤n,m≤1,000,000,000。 
题解 
题目定位:签到题  
考察内容:找规律  
题解:很容易发现只有 m,n 为奇数时才能找到中心,然后模拟统计即可  
参考代码   https://paste.ubuntu.com/26522988/ 
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;


long long chen(int n)
{
     int i;
     long long s=1;
     for (i=1;i<=n;++i)
         s*=4;
     return s;



int main()
{
      int n,m,i=0;
      long long s=0;
      scanf("%d %d",&n,&m);
      while (n>1 || m>1){
            if (n%2==0||m%2==0) break;
s+=chen(i);
n/=2; m/=2;
            i++;
      }
      if (m==1&&n==1)s+=chen(i);
      cout<<s<<endl;
 return 0; 

}

C.竞赛机巧 
题目描述  在 ACM 竞赛中,当遇到有两个队伍(人) 解出相同的题目数量的时候,我们需要通过他 们解决问题的总时间进行排序。 一共有 N(1<=N<=5,000)条时间被以时(0<=Hours<=99), 分(0<=Minutes<=59),秒 (0<=Seconds<=59)的形式记录。 
你必须要把他们按时,分,秒排序为 升序,最少的时间最先。 考虑到如下的样例,这三 个解出相同题目数量的时间为 11:20:20  11:15:12  14:20:14  正确的排序结果应该是这样的:  11:15:12 11:20:20  14:20:14 输入描述: 
第 1 行,一个整数 N 第 2~n+1 行,每行 3 个整数,表示时,分,秒 
输出描述: 
共 n 行,每行 3 个整数,表示排序完后的结果 
示例 1 输入 
3  
11 20 20 
11 15 12 
14 20 14 输出 
11 15 12  
11 20 20  
14 20 14 说明 
所以在保证能做对的情况下,我们应当尽量减少罚时 题解 
题目定位:签到题  
考察内容:排序  
题解:为了体现对萌新的友好,本题没有卡掉冒泡排序的做法,当然推荐使用 sort() 排序 减少做题时间以及时间复杂度。 
复杂度 O(nlogn)  
参考代码  
冒泡排序: https://paste.ubuntu.com/26522992/  

Sort(): https://paste.ubuntu.com/26522996/ 

#include<iostream>  
#include<algorithm>  
#include<cstdio>


using namespace std;  


int n;  


struct Time {     
         int h,m,s;  
} t[5000];  


bool comp (Time t1, Time t2) {  
     return t1.h < t2.h ||   
        (t1.h == t2.h && t1.m<t2.m) ||   
        (t1.h == t2.h && t1.m == t2.m && t1.s<t2.s);       
}  


int main () {  


     cin >> n;  
     for (int i = 0; i < n; i++)  
         cin >> t[i].h >> t[i].m >> t[i].s;  


     for (int i = 0; i < n-1; i++) {  
         int min = i;  
         for (int j = i+1; j < n; j++)  
             if (comp(t[j],t[min])) min = j;  
         Time tmp = t[i];  
         t[i] = t[min],t[min] = tmp;  
     }  




     for (int i = 0; i < n; i++)  
            cout << t[i].h << " " << t[i].m <<" " << t[i].s << "\n";  


return 0;

扫描二维码关注公众号,回复: 1035282 查看本文章

#include<iostream>  
#include<algorithm>  
#include<cstdio>


using namespace std;  


int n;  


struct Time {     
         int h,m,s;  
} t[5000];  


bool comp (Time t1, Time t2) {  
     return t1.h < t2.h ||   
        (t1.h == t2.h && t1.m<t2.m) ||   
        (t1.h == t2.h && t1.m == t2.m && t1.s<t2.s);       
}  


int main () {  


     cin >> n;  
     for (int i = 0; i < n; i++)  
         cin >> t[i].h >> t[i].m >> t[i].s;  


     for (int i = 0; i < n-1; i++) {  
         int min = i;  
         for (int j = i+1; j < n; j++)  
             if (comp(t[j],t[min])) min = j;  
         Time tmp = t[i];  
         t[i] = t[min],t[min] = tmp;  
     }  




     for (int i = 0; i < n; i++)  
            cout << t[i].h << " " << t[i].m <<" " << t[i].s << "\n";  


return 0;

D.训练技巧 
题目描述  常州大学组织了新生寒假训练一共 N 天,每天训练可以获得的训练效果是 Ei。但是如果连 续训练超过 K 天,萌新们会受不了而被劝退。 现在负责人想知道,如何安排能保证萌新不会被劝退并且能获得最大的训练效果。 输入描述: 
第一行:两个用空格隔开的整数:N 和 K,1≤N≤100000,1≤K≤N 第二行到 N+1 行:第 i+1 行有一个整数,表示第 N 天的训练效果是 Ei,(0 <= Ei <= 1,000,000,000) 
输出描述: 
第一行:单个整数,表示最大的能力之和 
示例 1 输入 
5 2  



4  
5 输出 
12 说明 
(除了第三天以外每天都在训练,总训练效果为 1+2+4+5=12) 备注: 
1≤n≤100,000 
题解 
题目定位:算法题  
考察内容:动态规划,单调队列  
题解:这题应该是个经典的动态规划问题(原题),用单调队列优化,本题对萌新似乎不 是 很友好,因为手上刚好有这个题的标程和数据,就拿来凑数了。 用 dp[i]表示不取 i,且取法合法的最小损失,那么 dp[i]=min{dp[j]+a[i]},其中 i-j<=k。 注意到
可以用单调队列优化。我们用 q[i]表示目前符合条件的位置,直接更新即可。 开 long long,ans 初始值要足够大~  
复杂度 O(nlogn)  
参考代码 

https://paste.ubuntu.com/26523089/

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long


int n,k,st,ed,q[100001];
ll dp[100001],a[100001],ans,tot;


ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}


int main()
{
n=read();k=read();ans=99999999999999999ll;
for(int i=1;i<=n;i++) a[i]=read(),tot+=a[i];
for(int i=1;i<=n;i++)
{
dp[i]=a[i]+dp[q[st]];
while(st<=ed && dp[q[ed]]>dp[i]) ed--;q[++ed]=i;
while(q[st]<i-k) st++;
}
for(int i=n-k;i<=n;i++) ans=min(ans,dp[i]);
printf("%lld\n",tot-ans);
return 0;

}

E.这是一个数学题 
题目描述  已知有一个 n+1 个数的数列,对于给定的 A0 和An ,当 i 满足当 1<=i<=n-1 时有  
 
现在小星想知道对于这个数列一段区间的和。 
 
输入描述: 
第一行输入四个数 n,A0,An,Q 
接下来 Q 行 每行输入两个数 l,r 
0=< n,A0,An<=1e9,Q<=100000 
0<=l<=r<=n 
输出描述: 
对于每组查询输出 Al到 Ar 的和 
示例 1 输入 
3 0 3 2 
1 1 
1 3 输出 

6 备注: 
为了对萌新表现出友好,数据保证了对于 Ai 的每一项都是整数 
题解 
题目定位:数学题  
考察内容:简单数学公式  
题解:考虑将两边组合数化简于是得到 
 
熟悉的同学应该能一眼发现 Ai 是个等差数列,公差 
 
接下来查询就是一个等差数列求和,运用等差数列求和公式即可复杂度 O(1)  
由于这题没有设置取模,所以在乘的时候要小心爆 long long  
这题保证了 d 是整数 事实上这个结论可以加强为  
Ai 是等差数列的充要条件是: 
 
有兴趣的可以证明下。  
参考代码  
https://paste.ubuntu.com/26523141/

#include<bits/stdc++.h>
#define cl(a,b) memset(a,b,sizeof(a))
#define debug(a) cerr<<#a<<"=="<<a<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;


const int maxn=1e5+10;


int n,a0,an,q,p;


void query(int l,int r)
{


    ll al=a0+1ll*p*l;
    ll ar=a0+1ll*p*r;
    ll sum=(al+ar)*(r-l+1)/2;
    printf("%lld\n",sum);
}


int main()
{
    while(~scanf("%d%d%d%d",&n,&a0,&an,&q))
    {
        p=(an-a0)/n;
        int l,r;
        while(q--)
        {
            scanf("%d%d",&l,&r);
            query(l,r);
        }
    }
    return 0;

}

F.大佬的生日大礼包 
题目描述  今天是某不愿透露姓名的谈姓大佬的生日,转发这场比赛到三个群就可以,获得以下三种 礼包之一。 豪华礼包:一个 U 盘、一个鼠标和一个机械键盘。 幸运礼包:一个 U 盘、两个鼠标。 普通礼包:两个 U 盘、一个鼠标。 大佬一共准备了 a 个 U 盘、b 个鼠标和 c 个机械键盘。为了给更多的人带来足够多的惊 喜,大佬希望相邻的两位领礼包的参赛选手拿到的礼包类型都是不同的。 
由于大佬正在宴请 Final 选手,并没有空打理这些,所以想让你告诉他 这些奖品最多可以 发出多少份礼包。 输入描述: 
输入第一行包含一个正整数 T。 接下来 T 行每行包含 3 个正整数 a, b, c,依次表示 U 盘、鼠标和机械键盘各有多少个。 
输出描述: 
输出 T 行,每行一个整数,表示最多能发出多少份礼包。 
示例 1 输入 

4 4 0 
1 1 1 输出 

1 备注: 
T<=100000 0<=a,b,c<=1000000 
题解 
题目定位:贪心||二分  
考察内容:贪心,分类讨论,二分  
题解:首先观察题目可知,一共 1e5 个查询,这需要我们在 O(1)或者 O(logn) 级 别计算出结果。  
解法一:由于礼包只有三种,显然答案和 a,b,c 存在这公式的关系,所以分类讨 论几种 情况即可。  
解法二:我们发现只有豪华礼包才有键盘,而豪华礼包只需要 1 个 U 盘和 1 个鼠 标。 
所以我们可以通过二分答案或者二分豪华礼包的方法来得到答案  
参考代码  
分类讨论 
复杂度 O(1)  

https://paste.ubuntu.com/26523178/  

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T,a,b,c,s,t,ans,x,y,a1,b1,c1,ans1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d",&a,&b,&c);
        if (a<c) c=a;
        if (b<c) c=b;
        if (a<b)
        {
            t=a;
            a=b;
            b=t;
        }
        a1=a;b1=b;c1=c;
        t=a-b;
        ans1=0;ans=0;
        if (c<=t)
        {
            x=(c-1)*2+c;
            y=c-1+c;
            if (x<=a&&y<=b)
            {
                ans=c+c-1;
                a-=x;b-=y;
                x=min(a/3,b/3);
                ans+=x*2;
                a-=x*3;b-=x*3;
                if (a>=2&&b>=1) ans++;
            }
            else
            {
                x=min(a/3,b/2);
                a-=x*3;b-=x*2;
                ans=x*2;
                if (a>=1&&b>=1) ans++;
            }
        }
        else
        {
            x=t*2+t;
            y=t+t;
            if (x<=a&&y<=b)
            {
                ans=t+t;
                a-=x;b-=y;
                c-=t;
                x=min(a/5,b/5);
                x=min(x,c/2);
                ans+=x*4;
                a-=x*5;b-=x*5;c-=x*2;
                if (a>=1&&b>=1&&c>=1)
                {
                    a--;b--;c--;
                    ans++;
                }
                x=min(a/3,b/3);
                ans+=x;
                a-=x*3;b-=x*3;
                if (a>=2&&b>=1) ans++;
            }
            else
            {
                x=min(a/3,b/2);
                a-=x*3;b-=x*2;
                ans=x*2;
                if (a>=1&&b>=1) ans++;
            }
        }
        ans1=ans;ans=0;
        a=a1;b=b1;c=c1;t=a-b;
        if (t>=c)
        {
            x=c*2+c;
            y=c+c;
            if (x<=a&&y<=b)
            {
                ans=c+c;
                a-=x;b-=y;
                x=min(a/3,b/3);
                ans+=x*2;
                a-=x*3;b-=x*3;
                if (a>=2&&b>=1) ans++;
            }
            else
            {
                x=min(a/3,b/2);
                a-=x*3;b-=x*2;
                ans=x*2;
                if (a>=2&&b>=1) ans++;
            }
        }
        else
        {
            x=t*2+t;
            y=t+t;
            if (x<=a&&y<=b)
            {
                ans=t+t;
                a-=x;b-=y;
                c-=t;
                x=min(a/5,b/5);
                x=min(x,c/2);
                ans+=x*4;
                a-=x*5;b-=x*5;c-=x*2;
                if (a>=3&&b>=2&&c>=1)
                {
                    a-=3;b-=2;c--;
                    ans+=2;
                }
                x=min(a/3,b/3);
                ans+=x*2;
                a-=x*3;b-=x*3;
                if (a>=1&&b>=2) ans++;
            }
            else
            {
                x=min(a/3,b/2);
                a-=x*3;b-=x*2;
                ans=x*2;
                if (a>=2&&b>=1) ans++;
            }
        }
        ans=max(ans,ans1);
        a=a1;b=b1;c=c1;
        if (c==0)
        {
            x=min(a/3,b/3);
            ans=x*2;
            a-=x*3;b-=x*3;
            if (a>=2&&b>=1) ans++;
        }
        printf("%d\n",ans);
    }

}

include<bits/stdc++.h>
using namespace std;


#define LL long long
#define LD long double
#define For(i,j,k) for (int i=j;i<=k;++i)
#define foR(i,j,k) for (int i=j;i>=k;--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define sqr(x) (x)*(x)
template <typename T>void cmin(T &a,T b){a=min(a,b);}
template <typename T>void cmax(T &a,T b){a=max(a,b);}
template <typename T>
void read(T &x){
char c;T f=1;
while (!isdigit(c=getchar())) if (c=='-') f=-1;
x=c-'0';
while (isdigit(c=getchar())) x=x*10+c-'0';
x*=f;
}
int main(){
freopen("store.in","r",stdin);
freopen("store.out","w",stdout);
int T;
read(T);
For (i,1,T){
int a,b,c;read(a),read(b),read(c);
int l=0,r=0x7fffffff;
while (l+1<r){
int mid=(l+r)>>1,A=a-mid,B=b-mid,C=c;
if (A>=0&&B>=0&&A+B+C>=mid&&(A+B)>=mid>>1&&(B+C)>=mid>>1&&(A+C)>=mid>>1)
l=mid;
else r=mid;

printf("%d\n",l); 
}
return 0;

}

G.零下 e 度 
题目描述  在家好冷! 又多冷呢? 大概是零下 e 度! 为什么是零下 e 度呢? 不知道,因为我编不下去了。 求给定一个数 n,求出最接近 n!/e 的整数 
 
输入描述: 
一行一个整数 n 1<=n<=10^8 
输出描述: 
一行一个整数,即题目描述中所求,由于这个数字可能很大,我们只需要知道 mod 99824 4353 后的结果(出题人负责任地告诉你,这个数字是个质数) 
示例 1 输入 
6 输出 
265 示例 2 输入 
87 输出 
158005593 示例 3 输入 
16777216 输出 
16065816 题解 
题目定位:数学题 公式题 结论题  
考察内容:组合数学 递推  
题解: 
我们将 n!/e 分成两部分 n!的意义是 n 个数的全排列, 
由于 1/e = e^(-1) = 1/0! - 1/1! + 1/2! - ..... + (-1)^n/n! + Rn(-1), 
其中 Rn(-1)是余项,等于(-1)^(n+1) * e^u / (n+1)!,且 u∈(-1, 0). 
相乘就是一个错排公式 D(n) = n! (1/0! - 1/1! + 1/2! - 1/3! - ..... + (1)^n/n!), 
所以,D(n) = n! * e^(-1) - (-1)^(n+1) * e^u / (n+1), u∈(-1, 0). 
而|n! Rn| = |(-1)^(n+1) * e^u / (n+1)| = e^u / (n+1) ∈ (1/[e(n+1)], 1/(n+1)),可知即使在 n=1 时,该余项(的绝对值)也小于 1/2。 
因此,无论 n! Rn 是正是负,n! / e + 1/2 的整数部分都一定与 M(n)相同。 
所以这题就变成了求 n 位数的错排 
对于 n<=1e8 的单组查询,我们有递推式 
D(n) = (n-1) [D(n-2) + D(n-1)] 
显然不能用数组存,维护三个变量更新即可 

注意取模的常数较大尽量减少取模即可 

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,i,j,k,f;
    long long x,y,q=998244353,ans,s;
    scanf("%d",&n);
    if (n==1) ans=0;
    if (n==2) ans=1;
    x=0;
    y=1;
    for (i=3;i<=n;i++)
    {
        s=(x+y)*(i-1)%q;
        x=y;
        y=s;
    }
    if (n>2) ans=y;
    cout<<ans<<endl;

}

H.酸碱滴定 
题目描述  有时候你会抱怨,什么时候才能到终点。 有时候你会迫不及待,怎么颜色还不改变。 滴定管长场的,我们的路长长的。 用心的放入每一滴,终点就在你手心。 今天小星需要去完成一个酸碱滴定实验。,实验室老师要求用 A mol/L 的 HCL 去测定一瓶 NaOH 的浓度。首先小星取出了一个锥形瓶,在里面放入的 Bml 的 NaOH,并滴加 1-2 滴 甲基橙,然后用 HCL 去滴定他 当滴定至恰好变为红色时(可以认为 H 离子浓度等于 OH 离子浓度),用了 C ml 小星现在需要计算 NaOH 的浓度是多少?单位(mol/L) 对于浓度采用化学里“四舍六入五成双”方法保留 2 位小数 规则 1 :第三位小数≤4 时舍去 规则 2: 第三位小数≥6 时进上 规则 3: 第三位小数等于 5 时 3.1 首先根据 5 后面的数字来定,当 5 后有数时,舍 5 入 1; 3.2 当 5 后无有效数字时,需要分两种情况来讲:     3.2.1   5 前为奇数,舍 5 入 1;     3.2.2  5 前为偶数,舍 5 不进(0 是偶数) 例如 9.8249=9.82    规则 1  9.82671=9.83  规则 2 9.82501=9.83  规则 3.1 9.8351 =9.84  规则 3.1 9.8350=9.84    规则 3.2.1 9.8250=9.82   规则 3.2.2 
 
 
输入描述: 
输入第一行一个数 T(T<=20)表示数据组数 对于每组数据输入 A,B,C 三个 3 位小数 0.000<a,b,c<50.000 
输出描述: 
结果“四舍六入五成双”保留 2 位小数 
示例 1 输入 

10.000 10.000 1.825 
10.000 10.000 9.835 
1.010 21.325 19.823 
输出 
1.82 
9.84 
0.94 
说明 
样例 1 中计算出的结果为 1.8250000000 根据(规则 3.2.2) 答案应该为 1.82 9.835->9.84(规则 3.2.1) 
题解 
题目定位:模拟  
考察内容:细节处理  

题解:答案就是 a*c/b,然后根据规则分类讨论下就好,注意进位。 

#include <bits/stdc++.h>
using namespace std;


void solve(){
    double a, b, c;
    cin >> a >> b >> c;
    double ans = (a * c )* 100.0 / b;
    if( ans-(int)ans < 0.499 ) ans = (int)ans / 100.0;
    else if ( ans-(int)ans >0.501 ) ans = ((int)ans+1) / 100.0;
    else{
        if( ((int)ans)%2 ) ans = ((int)ans+1) / 100.0;
        else ans = (int)ans / 100.0;
    }
    printf("%.2lf\n", ans);
    return;
}


int main(){
    int n;
    cin >> n;
    while(n--){
        solve();
    }
    return 0;

}

I. 合成反应 
题目描述  有机合成是指从较简单的化合物或单质经化学反应合成有机物的过程。 有时也包括从复杂原料降解为较简单化合物的过程。 由于有机化合物的各种特点,尤其是碳与碳之间以共价键相连,有机合成比较困难,常常要 用加热、光照、加催化剂、加有机溶剂甚至加压等反应条件。 但是前人为有机合成提供了许多宝贵的经验。 现在已知有 K 总物质和 N 个前人已经总结出的合成反应方程式 小星想知道在现有 M 种物质的情况下 能否合成某些物质。 输入描述: 
第一行输入四个整数 K,N,M,Q(K,N,M,Q<=1e5) K 表示一共 K 总物质 接下来 N 行 每行三个数字 a b c(任意两个数可能相等) 
表示 a 和 b 反应可以生成 c 反应是可逆的 即可以通过 c 可以分解出 a 和 b 接下来一行行然后输入 m 个数,表示 m 种原料(每一种原料都可以认为有无限多) 接下来 Q 个行 Q 个询问 对于每个询问 输出一个数字 x 判断是否可以通过一些反应得到第 x 
输出描述: 
可以得到 Yes 否则 No 
示例 1 输入 
10 3 4 10 
1 2 3 
4 5 6 
2 5 7 
3 4 5 8 









10 
输出 
Yes 
Yes 
Yes 
Yes 
Yes 
Yes 
Yes 
Yes 
No 
No 
说明 
一共 10 总物质有第 3,4,5,8 四种原料 查询每一种是否可以通过反应得到 首先通过 3 可以分解得到 1 2 然后 4 5 合成 6 2 5 合成 7 于是除了 9 10 都可以得到 
题解 
题目定位:图论||搜索剪枝  
考察内容;bfs 暴力剪枝  
题解:这道题灵感来源是有机化学中的合成题。  
解法一: 首先的一个最简单想法是每次对所以配方做一次分解,一次合成,然后做到无法 做很不幸这样的做法被出题人卡掉了,但是我们可以尝试剪枝,剪过去 每次优先考虑分 解,一旦发现合成新的物质就先去分解,然后检查一下能否 合成这两步无限循环,发现无 法合成就跳出,这样复杂的是均摊 O(logn)级别 的,出题人水平有限卡不掉。复杂的 一共是 O(logn) 事实证明,出题人并没有卡掉各种暴力,稍微剪枝下就能过  
参考代码  
https://paste.ubuntu.com/26523319/  
解法二: 标程的做法是 bfs 我们考这样一个有向图对于每个配方建四条边 c->b 权值 为 a , c->a 权值为 b a->c 和 b->c 权值为-1 比如输入的配方是(a, b),能合 成 c 然后建四条边 c->b 权值为-1 c->a 权值为-1 a->c 权值为 b b->c 权值为 a 然后做一次 bfs 首先把原料都丢进去队列,然后每次到一个点访问它的后 继 如果它和它 的权值都是有的那么就丢进去 例如节点 x 能到节点 y 的条件是他们之间的有向边的权 值 z 是-1 或者 zx 存在原 料库当中的 这样复杂度大概是 O(N+M)  
参考代码  

https://paste.ubuntu.com/26523337/ 

#include<bits/stdc++.h>
using namespace std;
struct sam
{
    int x,y,z;
};
int n,i,j,k,s,t,m,q,top,tail,f;
sam a[100010];
int b[100010],c[200010],d[100010][3];
int cmp(sam &p,sam &q)
{
    return (p.z<q.z);
}
int work(int t)
{
    int ans;
    if (d[t][1]==0&&d[t][2]==0) return 0;
    ans=d[t][1];
    while (ans<=d[t][2])
    {
        if (b[a[ans].x]==0)
        {
            b[a[ans].x]=1;
            top++;
            c[top]=a[ans].x;
        }
        if (b[a[ans].y]==0)
        {
            b[a[ans].y]=1;
            top++;
            c[top]=a[ans].y;
        }
        ans++;
    }
    return 1;
 //   b[t]=2;
}
int main()
{
    scanf("%d%d%d%d",&k,&n,&m,&q);
    for (i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    }
    sort(a+1,a+1+n,cmp);
    memset(d,0,sizeof d);
    for (i=1;i<=n;i++)
    {
        t=a[i].z;
        if (d[t][1]==0) d[t][1]=i;
        d[t][2]=i;
    }
    tail=1;top=0;
    for (i=1;i<=m;i++)
    {
        scanf("%d",&t);
        if (b[t]==0)
        {
            b[t]=1;
            top++;
            c[top]=t;
        }
    }
    while (tail<=top)
    {
        work(c[tail]);
        tail++;
    }
    f=0;
    while (f==0)
    {
        f=1;
        for (i=1;i<=n;i++)
            if (b[a[i].x]>0&&b[a[i].y]>0&&b[a[i].z]==0)
        {
            f=0;
            b[a[i].z]=1;
            top++;
            c[top]=a[i].z;
            while (tail<=top)
            {
                work(c[tail]);
                tail++;
            }
        }
        if (f==1) break;
        f=1;
        for (i=n;i>=1;i--)
            if (b[a[i].x]>0&&b[a[i].y]>0&&b[a[i].z]==0)
        {
            f=0;
            b[a[i].z]=1;
            top++;
            c[top]=a[i].z;
            while (tail<=top)
            {
                work(c[tail]);
                tail++;
            }
        }
    }
    for (i=1;i<=q;i++)
    {
        scanf("%d",&t);
        if (b[t]==0) printf("No\n"); else printf("Yes\n");
    }

}

#include<bits/stdc++.h>
using namespace std;
int a[100010];
struct hxfy
{
    int x,y,z;
} b[100010];
int c[100010];
struct node
{
    int x,v;
};
vector<node> v[100010];
queue<int> Q;
int vis[100010];
void bfs()
{
    while(!Q.empty())
    {
        int f=Q.front();
      //  cout<<f<<endl;
        Q.pop();
        for(int i=0; i<v[f].size(); i++)
        {
            
            if(v[f][i].v==-1)
            {
                if(vis[v[f][i].x]==0)
                {
                    a[v[f][i].x]=1;
                    Q.push(v[f][i].x);
                    vis[v[f][i].x]=1;
                }
            }
            else
            {
                if(a[v[f][i].v])
                {
                    if(vis[v[f][i].x]==0)
                    {
                        Q.push(v[f][i].x);
                        vis[v[f][i].x]=1;
                         a[v[f][i].x]=1;
                    }


                }
            }
        }
    }


}
int main()
{
  //freopen("1","r",stdin);
  // freopen("2","w",stdout);
    int k,n,m,q;
    scanf("%d%d%d%d",&k,&n,&m,&q);
    for(int i=1; i<=n; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        b[i].x=x;
        b[i].y=y;
        b[i].z=z;
        v[x].push_back((node)
        {
            z,y
        });
        v[y].push_back((node)
        {
            z,x
        });
        v[z].push_back((node)
        {
            y,-1
        });
        v[z].push_back((node)
        {
            x,-1
        });


    }
    memset(a,0,sizeof(a));
    memset(c,0,sizeof(c));
    memset(vis,0,sizeof(vis));
    for(int i=1; i<=n; i++)
    {
        c[b[i].z]=i;
    }
    for(int i=1; i<=m; i++)
    {
        int x;
        scanf("%d",&x);
        a[x]=1;
        Q.push(x);
    }
    bfs();
    for(int i=1; i<=q; i++)
    {
        int x;
        scanf("%d",&x);
        if(a[x])
            printf("Yes\n");
        else
            printf("No\n");
    }

}

J.同分异构体 
题目描述  化学上,同分异构体是一种有相同分子式而有不同的原子排列的化合物。简单地说,化合物 具有相同分子式,但具有不同结构的现象,叫做同分异构现象; 输入一个数 n,求 n 个碳的烷烃(仅由碳、氢、碳碳单键与碳氢单键所构成)的同分异构体 的数目 n=3,4,5 如下图所示 
 
输入描述: 
输入一个数 n(n<=9) 
输出描述: 
一个整数表示答案 
示例 1 输入 
3 输出 
1 示例 2 输入 
4 输出 
2 示例 3 输入 
5 输出 
3 备注: 
这里不考虑空间异构 
题解 
题目定位:构造 打表 百度 毒瘤题  
考察内容: 百度 打表  
题解:根据常识,同分异构体数是没有公式或者递推式的,所以我们稍微百度一下就能知 道 前 30 项是什么,但是为了让题不那么毒瘤,出题人把数据范围出成了 n<=9,n=9 的时候答 案是 35,保证了,不百度的情况下,能手推构造出来。  
参考代码:  

https://paste.ubuntu.com/26523362/ 

#include<bits/stdc++.h>
using namespace std;
int a[11]={1,1,1,2,3,5,9,18,35};
int main()
{
    int n;
   cin>>n;
   cout<<a[n-1]<<endl;


}

猜你喜欢

转载自blog.csdn.net/huzi99/article/details/79292589
今日推荐