程序设计与算法基础3-7

巡逻的士兵

Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。
为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,
或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现要求统计按这样的方法,
总共可能有多少种不同的正好三个士兵去侦察的士兵组合方案。
注: 按上法得到少于三士兵的情况不统计。
1 <= N <= 2的32次方-1
输入格式
有多行(可能有上百行,尽量优化代码),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,输出针对N的方案数
直到没有数字
输入样例
10
4
0
输出样例
2
0

#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
map<ll,ll>fa;

ll f(ll n)
{
    if(n<3) return 0;
    if(n==3)  return 1;
    else if(fa.find(n)!=fa.end()) return fa[n];
    else return fa[n] = f(n/2)+f((n+1)/2);
}

int main()
{
    ll n;
    while(scanf("%lld",&n)&&n)
    {
        cout<<f(n)<<endl;
    }

    return 0;
}

偷懒的士兵

Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。
为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,
或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现有一个“聪明”的士兵,
经常通过选择站在合适的初始位置,成功避免被选中去侦察。这引起了陈教官的注意。陈教官希望你编写一个程序,
当给定士兵数之后,输出有多少个位置上的士兵是不可能被选中去巡逻的。
注: 按上法得到少于三士兵的情况不用去巡逻。
1 <= N <= 21亿
输入格式
有多行(可能有上百行,请尽量优化代码),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,不可能被选中去巡逻的位置数
直到没有数字
输入样例
10
6
0
输出样例
4
0

#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
map<ll,ll> fa;

ll f(ll n)
{
    if (n < 3) return n;
    if (n == 3) return 0;
    else if (fa.find(n) != fa.end()) return fa[n];
    else return fa[n] = f(n / 2) + f((n + 1) / 2);
}

int main()
{
    ll n;
    while (scanf("%lld", &n) && n)
    {
        cout << f(n) << endl;
    }
    return 0;
}

偷懒的士兵2

Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。
为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,
或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现有一个“聪明”的士兵,
经常通过选择站在合适的初始位置,成功避免被选中去侦察。这引起了陈教官的注意。陈教官希望你编写一个程序,
当给定士兵数之后,输出不可能被选中去巡逻的最少编号位置(如果不存在不可能被选中的位置,则输出0)。
注: 按上法得到少于三士兵的情况不用去巡逻。
1 <= N <= 100000
输入格式
有多行(不多于20行),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,不可能被选中去巡逻的最小位置
直到没有数字
输入样例
9
6
0
输出样例
2
0

#include <iostream>
using namespace std;
int big = 0x3f3f3f;
int f(int minn,int num,int dis)
{
    if(num<3) return minn;
    if(num==3) return big;
    else return min(f(minn,(num+1)/2,dis*2),f(minn+dis,num/2,dis*2));
}

int main()
{
    int n;
    while(scanf("%d",&n)&&n)
    {
        int ans = f(1,n,1);
        if(ans==big) cout<<0<<endl;
        else cout<<ans<<endl;
    }
    return 0;
}

除法等式

Description
输入正整数n,按从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中aj各代表09中的一个数字
除了0可以重复外,其它数字不能重复,2<=n<=90000。
输入格式
多case,每行一个数字,最后一个数字是0
输出格式
除了最后一行0不用处理,
其它每个case,按被除数由小到大输出所有满足等式的情况
注:如果没有满足条件的等式,该case结束后,也需要输出一个空行
两个case之间用一个空行分隔
输入样例
44
666
6666
20000
0
输出样例
00176/00004=44
00308/00007=44
00352/00008=44
00572/00013=44
00704/00016=44
00792/00018=44
00836/00019=44
01056/00024=44
01408/00032=44
01584/00036=44
01628/00037=44
01672/00038=44
01760/00040=44
01892/00043=44
01980/00045=44
02068/00047=44
02156/00049=44
02376/00054=44
02948/00067=44
03080/00070=44
03168/00072=44
03256/00074=44
03520/00080=44
03564/00081=44
03740/00085=44
04180/00095=44
04268/00097=44
04312/00098=44
04620/00105=44
04752/00108=44
05720/00130=44
05896/00134=44
05984/00136=44
06028/00137=44
06072/00138=44
06380/00145=44
06908/00157=44
07040/00160=44
07392/00168=44
07920/00180=44
08360/00190=44
08536/00194=44
08976/00204=44
09108/00207=44
09460/00215=44
09504/00216=44
09548/00217=44
10560/00240=44
10780/00245=44
13068/00297=44
14080/00320=44
15268/00347=44
15840/00360=44
16280/00370=44
16720/00380=44
16940/00385=44
17600/00400=44
17820/00405=44
17952/00408=44
18700/00425=44
18920/00430=44
19008/00432=44
19800/00450=44
20108/00457=44
20680/00470=44
20900/00475=44
21560/00490=44
21780/00495=44
23760/00540=44
26004/00591=44
27940/00635=44
29480/00670=44
30140/00685=44
30712/00698=44
30800/00700=44
31020/00705=44
31680/00720=44
31856/00724=44
31900/00725=44
32560/00740=44
35024/00796=44
35200/00800=44
35640/00810=44
35904/00816=44
37400/00850=44
40260/00915=44
40700/00925=44
41008/00932=44
41800/00950=44
42108/00957=44
42680/00970=44
43120/00980=44
45628/01037=44
45672/01038=44
46200/01050=44
47520/01080=44
47652/01083=44
57200/01300=44
58476/01329=44
58960/01340=44
59268/01347=44
59840/01360=44
60280/01370=44
60720/01380=44
60940/01385=44
63800/01450=44
65032/01478=44
67408/01532=44
67892/01543=44
69080/01570=44
69432/01578=44
70048/01592=44
70400/01600=44
72380/01645=44
73920/01680=44
74052/01683=44
79200/01800=44
79420/01805=44
83600/01900=44
84700/01925=44
85360/01940=44
89760/02040=44
90068/02047=44
90376/02054=44
91080/02070=44
91300/02075=44
91740/02085=44
94600/02150=44
95040/02160=44
95348/02167=44
95480/02170=44

27306/00041=666
41958/00063=666
43290/00065=666
52614/00079=666
53946/00081=666
63270/00095=666

20000/00001=20000
40000/00002=20000
60000/00003=20000
80000/00004=20000

提示
提示:6666没有找到满足条件的等式

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 15;
int norepeat(int s,int i)
{
    int cnt=0;
    int flag=0;
    int j=0;
    int t[N];
    while(s>0)
    {
        t[j++]  = s%10;
        cnt++;
        s/=10;
    }
    while(i>0)
    {
        t[j++] = i%10;
        cnt++;
        i/=10;
    }
    sort (t,t+cnt);
    for(int j=0;j<cnt;j++)
    {
        if(t[j]!=0&&t[j]==t[j-1])
        {
           return 0;
        }
    }
    return 1;
}

void answer(int n)
{
    int i=1,s;
    s = i*n;
    for(int i=1;i<=99999;i++)
    {
        s = i*n;
        if(s>=100000) break;
        else if(norepeat(s,i)==1)
            printf("%05d/%05d=%d\n",s,i,n);
    }
    cout<<endl;



}

int main()
{
    int n;
    while(scanf("%d",&n)&&n)
    {
        answer(n);
    }
    return 0;
}

三角形

Description
著名的数学家毕达哥拉斯可能从来都不曾想过有人居然会问他这样的一个问题:给出一个整数,存在多少个直角三角形,
它的某一条边的长度等于这个整数,而且其他边的长度也是整数。既然毕达哥拉斯不可能预见到有计算机的出现,
如果他回答不出来,那谁又能责怪他呢?但是现在既然你有了计算机,那么回答不出来就说不过去了。
输入格式 第一行有一个整数n,代表有多少个数据(1<=n<=20)。接下来有n行,每行代表一个数据。一个数据就是一个整数ai(a<=i<=n,1<=ai<=100)。
输出格式
每个数据都必须有相应的输出。两个数据的输出之间有一个空行。
对于每一个数据,如果找不到解,则输出一个空行。如果找到解,就把符合条件的所有直角三角形输出。每个三角形占一行,输出该三角形的另外两条边,
必须先输出长边,然后一个逗号,再输出短边。两个三角形之间不能有空行,而且必须按照长边降序排列。
输入样例
2
20
12
输出样例
101,99
52,48
29,21
25,15
16,12

37,35
20,16
15,9
13,5

#include <iostream>
#include <algorithm>
using namespace std;

int main()
{

    int n;
    cin >> n;
    while (n--)
    {
        int x;
        cin >> x;
        for (int i = 2501; i >= 1; i--)
        {
            for (int j = i-1; j >= 1; j--)
            {
                if ((i * i + j * j == x * x)||( j * j + x * x == i * i))
                    printf("%d,%d\n", i, j);
            }
        }
        cout << endl;

    }
    return 0;
}

龙龙

Description
在比赛的时候,1Y(1 次AC)是很值得高兴的事情。但很多大牛总会因为很弱智的错误先WA 一次,再AC。
而很多时候,这点罚时的差距使得他们与金牌无缘。弱智错误系列中最显著的就是忘记加龙龙。
龙龙外国人叫它作long long,表示64位整数,输入与输出64位整数则可以使用例如
scanf(“%lld”, &a)与printf(“%lld”, a)的形式完成。很多图论和动态规划的题目中,
虽然题目说最后输出的答案是32 位的整数,但中间计算的过程有时会超过int,这时我们就要使用龙龙了。
可惜的是,很多同学刚开始学写程序都是用VC的,在VC上是无法使用long long的,我们要用__int64
代替,输入与输出64位整数则可以使用例如scanf(“%I64d”, &a)与printf(“%I64d”, a)的形式完成。
但是提交上OJ 的时候,如果使用GCC或G++,都只支持long long,我们在提交时又得按照上边的改回来(的确挺麻烦,窘)。
为了让知道龙龙的同学们记得使用龙龙,不知道的学习使用龙龙,下边有个很简单的函数,希望大家
求出它的返回值:
long long H(int n){
long long res = 0;
int i;
for(i = 1; i <= n; i=i+1 ){
res = (res + n/i);
}
return res;
}
不过直接使用这个函数是会超时的,必须改造这个函数,当然这一定难不到未来的编程高手–你
输入格式
第一行是数字T(T<=1021)表示case数,接下来T 行,每行一个整数n,n是一个32 位整数(保证可以由int 表示)。
输出格式
函数返回值。
输入样例
2
5
10
输出样例
10
27

#include <iostream>
using namespace std;
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int n;
        scanf("%d", &n);
        long long sum = 0;
        int tmp = 0;
        int cnt = 0;
        if (n > 0)
        {
            for (int i = 1;( tmp = (n / i - n / (i + 1))) != 0; i++)
            {
                cnt += tmp;
                sum += i * tmp;
            }
            for (int i = 1; i <= n - cnt; i++)
                sum += n / i;
        }
        printf("%lld\n", sum);
    }
    return 0;
}

分数拆分

Description
输入正整数k(k<=1000),将1/k变为不少于2项,但不多于3项的1/(xi)之和,xi为正整数,且i表示序号
注:请使用long long
输入格式
多case,一行一个整数k
最后一行是0
输出格式
对每一个case,按等式最右边一项分母,由小到大排序输出满足条件的等式,最右边一项分母相同,则按最右边第二项,依次类推
每一个case完成后,输出一个空行(没有满足的等式时,也要输出该空行)
输入样例
2
3
4
0
输出样例
1/2=1/6+1/3
1/2=1/42+1/7+1/3
1/2=1/24+1/8+1/3
1/2=1/18+1/9+1/3
1/2=1/15+1/10+1/3
1/2=1/12+1/12+1/3
1/2=1/4+1/4
1/2=1/20+1/5+1/4
1/2=1/12+1/6+1/4
1/2=1/8+1/8+1/4
1/2=1/10+1/5+1/5
1/2=1/6+1/6+1/6

1/3=1/12+1/4
1/3=1/156+1/13+1/4
1/3=1/84+1/14+1/4
1/3=1/60+1/15+1/4
1/3=1/48+1/16+1/4
1/3=1/36+1/18+1/4
1/3=1/30+1/20+1/4
1/3=1/28+1/21+1/4
1/3=1/24+1/24+1/4
1/3=1/120+1/8+1/5
1/3=1/45+1/9+1/5
1/3=1/30+1/10+1/5
1/3=1/20+1/12+1/5
1/3=1/15+1/15+1/5
1/3=1/6+1/6
1/3=1/42+1/7+1/6
1/3=1/24+1/8+1/6
1/3=1/18+1/9+1/6
1/3=1/15+1/10+1/6
1/3=1/12+1/12+1/6
1/3=1/21+1/7+1/7
1/3=1/12+1/8+1/8
1/3=1/9+1/9+1/9

1/4=1/20+1/5
1/4=1/420+1/21+1/5
1/4=1/220+1/22+1/5
1/4=1/120+1/24+1/5
1/4=1/100+1/25+1/5
1/4=1/70+1/28+1/5
1/4=1/60+1/30+1/5
1/4=1/45+1/36+1/5
1/4=1/40+1/40+1/5
1/4=1/12+1/6
1/4=1/156+1/13+1/6
1/4=1/84+1/14+1/6
1/4=1/60+1/15+1/6
1/4=1/48+1/16+1/6
1/4=1/36+1/18+1/6
1/4=1/30+1/20+1/6
1/4=1/28+1/21+1/6
1/4=1/24+1/24+1/6
1/4=1/140+1/10+1/7
1/4=1/42+1/12+1/7
1/4=1/28+1/14+1/7
1/4=1/8+1/8
1/4=1/72+1/9+1/8
1/4=1/40+1/10+1/8
1/4=1/24+1/12+1/8
1/4=1/16+1/16+1/8
1/4=1/36+1/9+1/9
1/4=1/18+1/12+1/9
1/4=1/20+1/10+1/10
1/4=1/15+1/12+1/10
1/4=1/12+1/12+1/12

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll k;
int main()
{
    ll x,  y,  z;
    while (~scanf("%d", &k) && k)
    {
        for (z = k + 1; z <= 3 * k; z++)
        {
            //两个
            if ((k * z) % (z - k) == 0)
            {
                y = k * z / (z - k);
                if (y >= z)
                {
                    cout << "1/" << k << "=" << "1/" << y << "+" << "1/" << z << '\n';
                }
            }
            //三个
            ll ymax = 2 * z * k / (z - k);
            ll ymin = k * z / (z - k) + 1;
            for (y = ymin; y <= ymax; y++)
            {
                if ((y * z * k) % (y * z - z * k - y * k) == 0)
                {
                    x = (y * z * k) / (y * z - z * k - y * k);
                    if (x >= y && y >= z)
                    {
                        cout << "1/" << k <<"=" << "1/" << x << "+" << "1/" << y << "+" << "1/" << z<<'\n';
                    }
                }
            }
        }
        cout << '\n';
    }
    return 0;
}

N皇后问题

Description
有N*N的国际象棋棋盘,要求在上面放N个皇后,要求任意两个皇后不会互杀,有多少种不同的放法?
输入格式
每一个数为T,代表CASE的数量,T<=13
此后,每行一个数N(13>=N>0)
输出格式
每一个CASE,输出对应答案
输入样例
2
4
5
输出样例
2
10

#include <iostream>
using namespace std;
int T;
const int N = 14;
int n;
bool col[N], sq[N * N], usq[N * N];
int ans = 0;
void dfs(int x)
{
    if (x > n) { ans++; return; }
    for (int i = 1; i <= n; i++)
    {
        if (!col[i] && !sq[x + i] && !usq[x - i + n]) {
            col[i] = sq[x + i] = usq[x - i + n] = 1;
            dfs(x + 1);
            col[i] = sq[x + i] = usq[x - i + n] = 0;
        }
    }
}
int main()
{
    cin >> T;
    while(T--)
    {
        cin >> n;
        dfs(1);
        cout << ans << endl;
        ans = 0;
    }
    return 0;
}

最小的特殊数字

Description
用全部N(N<=10)个0-9的数字组成一个“有效”整数(即没有前置0的整数),
求这些组成的数中能被K(0<K<10^10)整除的最小数字。
输入格式
输入分两行,第一行输入N, K,第二行输入N个数字。
输出格式
输出满足条件的最小的数(不含前置0),如果没有满足条件的数输出 -1。
输入样例
4 7
4 0 1 3
输出样例
1043
提示
413 % 7 = 0, 但是有前置0,所以满足条件的最小数是 1043 % 7 = 0。
此类题目需注意特殊情况,比如n=1时,如只输入一个0,答案只能是0。
注意long long

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 15;
ll a[N];
ll b[N];
ll n,k;
ll ans;
ll vis[N];
ll Answer()
{
    ll num = 0;
    for (int i = 0; i < n; i++)
    {
        num = num * 10 + b[i];
    }
    return num;
}

void dfs(int step)
{
    if (step == n)
    {
        if (Answer() % k == 0&&ans==0) //要求输出最小的那个
        {
            ans = Answer();
        }
        return;
    }
    for (int i = 0; i < n; i++)
    {
        if (a[i] == 0 && step == 0)
        {
            continue;
        }
        if (!vis[i])
        {
            b[step] = a[i];
            vis[i] = 1;

            dfs(step + 1);
            vis[i] = 0;
        }
    }
}

int main()
{
    cin >> n>> k;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    if (n == 1 && a[0] == 0)
    {
        cout << "0" << endl;
    }
    else
    {
        sort(a, a + n); //从小到大排序,保证答案是最小的那个
        dfs(0);
        if (ans == 0)
            cout << " - 1" << endl;
        else
            cout << ans << endl;
    }
    
    return 0;
}

快乐

Description
Lian是一个喜欢看动画片的人,自从成为ACMer(ACM爱好者)之后,他又迷上了网上做题。做题让他快乐,不过这也是需要付出精力的!!
假设有n道题,Lian做出第i道题后,他可以获得的快乐指数将增加gethappy[i],而消耗掉的精力将是losspow[i]。
假设Lian初始的快乐指数为1,精力为2000。可以理解,如果他消耗完了所有的精力那他得到再多的快乐都没有用。
你的任务就是帮他计算他所能得到的最多的快乐指数,且最后他依然有多余的精力(即至少为1)。
输入格式
第一行输入一个整数n,表示有n道题。(n<=50)
第二行输入n个整数,表示gethappy[1]到gethappy[n]
第三行输入n个整数,表示losspow[1]到losspow[n]。
输出格式
一个整数,表示Lian所能获得的最大快乐指数。
输入样例
3
15 23 61
350 1301 1513
输出样例
77

#include <iostream>
#include <algorithm>
using namespace std;
int dp[51][5100];
int main()
{
    int n;
    int happyindex = 1, energy=2000;
    cin>>n;
    int gethappy[51],losspow[51];
    for(int i=1;i<=n;i++)
    {
        cin>>gethappy[i];
    }
    for(int i=1;i<=n;i++)
    {
        cin>>losspow[i];
    }
    for(int i=1;i<=n;i++)
    {
       for(int j=1;j<=2000;j++)
       {

           if(losspow[i]<j)
           {
               dp[i][j] = max(dp[i-1][j],dp[i-1][j-losspow[i]]+gethappy[i]);
           }
           else if(losspow[i]>j)
           {
               dp[i][j] = dp[i-1][j];
           }
       }
    }
    cout<<dp[n][2000]+1;
    return 0;
}

01背包问题

Description
有一个容积为M的背包和N件物品。第i件物品的体积W[i],价值是C[i]。求解将哪些物品装入背包可使价值总和最大。每种物品只有一件,
可以选择放或者不放入背包。
输入格式
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2…N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
输出格式
一个数,表示最大总价值
输入样例
10 4
2 1
3 3
4 5
7 9
输出样例
12

#include <iostream>
using namespace std;

int w[201],c[201];

int dp[201][201];

int main()
{
    int m,n;
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i]>>c[i];
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            if(j>=w[i])
            {
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
            }
            else
                dp[i][j] = dp[i-1][j];
        }
    }
    cout<<dp[n][m];
    return 0;
}

最长上升子序列长度

描述:
当元素 ai1 < ai2 < … < aiK. 就说这个序列是有序上升的。
给定序列(a1, a2, …, aN),存在许多这样的子序列(ai1, ai2, …, aiK),
其中1 <= i1 < i2 < … < iK <= N.
也就是说,子序列是原序列允许挑选若干不连续的元素形成的序列。
举个例子,序列 (1, 7, 3, 5, 9, 4, 8) 就有许多个上上子序列,比如(1, 7), (3, 4, 8) 等。
所有这些上升子序列中最长的长度为4,比如 (1, 3, 5, 8).
你来编程实现,当给定一个初始序列,求解这个序列的最长上升子序列的长度。
输入格式:
此例包含多个测试cases(少于100组test cases)。
每一组test case包含2行。
第一行是这组case的序列长度 N。(N的范围0~10000)
第二行包含 N个整数的一个序列,用空格间隔这N个整数, 1 <= N <= 10000。
当N为0时,表示测试结束。
输出格式:
输出必须对每个test case,都有一个整数结果,表示这组case的最长上升子序列的长度。
输入样例:
7
1 7 3 5 9 4 8
6
1 8 3 6 5 9
5
1 2 3 4 5
0
输出样例:
4
4
5

#include <iostream>

using namespace std;

const int N = 1E4+10;
int dp[N],a[N];
int main()
{
     int n;
     while(cin>>n&&n)
     {
         int ans=1;
         for(int i=0;i<n;i++)
         {
             cin>>a[i];
             dp[i]=1;
         }
         for(int i=1;i<n;i++)
         {
             for(int j=0;j<i;j++)
             {
                 if(a[i]>a[j])
                 {
                     dp[i] = max(dp[i],dp[j]+1);
                 }
             }
             ans = max(ans,dp[i]);
         }
         cout<<ans<<endl;

     }
    return 0;
}

最长公共子序列长度

Description
给定两个字符串,请输出这两个字符串的最大公共子序列
输入格式
两行,一行一个字符串(不包括空格,Tab键),长度不超过1000
输出格式
输出最大公共子序列的长度
输入样例
abbca
aba
输出样例
3

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;
int dp[1001][1001];
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    int len1 = s1.size();
    int len2 = s2.size();
    for(int i=0;i<len1;i++)
        dp[i][0]=0;

    for(int i=0;i<len2;i++)
        dp[0][i]=0;

    for(int i=1;i<=len1;i++)
    {
        for(int j=1;j<=len2;j++)
        {
            if(s1[i-1]==s2[j-1])
                dp[i][j] = dp[i-1][j-1]+1;
            else
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
        }
    }
    cout<<dp[len1][len2];
    return 0;
}

万湖之国的形成

Description
N国原是一块平原上,没有湖,直到一颗小行星撞入大气层碎成成千上万的碎片,碎片再撞击地面形成
一个一个的坑, 下雨之后,最终形成万湖之国。
现在科学家想用计算机模拟万湖之国形成过程,假设每一块碎片撞击地面,都撞出一个园形坑,现在知道
每一个碎片造成的坑的圆心和半径,问每个坑都注满水后,最终形成多少个湖?
输入格式
第一行一个整数N,1<=N<=100,000,表示坑的数量
此后N行,每一行三个double实数,前两个数是圆心的坐标x和y,最后一个数是圆半径(不大于1000)
(数据随机产生,分布均匀)
输出格式
湖的个数
输入样例
3
0 0 5
10 0 5
11.1 0 2.5
输出样例
2

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

int f[100010];

struct Node
{
    double x, y, r;
};

bool cmp(Node a, Node b)
{
    return a.x + a.r < b.x + b.r;  //以圆的右端点排序
}

int find(int i)
{
    if (f[i] == i) return i;
    else
    {
        f[i] = find(f[i]);
        return f[i];
    }
}
Node node[100010];

int main()

{
    int n;
    cin >> n;
    int num = n;
    for (int i = 0; i < n; i++)
    {
        cin >> node[i].x >> node[i].y >> node[i].r; 
        f[i] = i;
    }
    sort(node, node + n, cmp);
    for (int i = 0; i < n; i++)
    {
        for (int j = i - 1; j >= 0; j--)
        {
            
            if (node[j].x + node[j].r <= node[i].x - node[i].r)   //前面一个圆的右端点比后面一个圆的左端点小,即当前圆与前面的圆不相交
                break;

            else if (pow((node[i].x - node[j].x), 2) + pow((node[i].y - node[j].y), 2) < pow((node[i].r + node[j].r), 2))  //相交
            {
                int a = find(i);
                int b = find(j);
                if (a != b)
                {
                    f[a] = b;
                    num--;
                }
            }
          
        }
    }
    cout << num;

    return 0;
}

繁忙的公路

Description
在一条笔直的大道(单方向行车道)上,汽车川流不息。道路从起点到终点,等距离的标记了1到N,
即起点是1,然后分别是2、3、4…,终点是N。每一个标记处,安装了智能探头,可以感知
在该点处车辆的增减数量。
一开始,整条道路上,没有车,然后,是不断接收到的智能探头发回的信息,格式如下:
H 5 9
H表明收到的是智能探头的回传信息,5表示标记5处的车辆信息,9表示该处车辆增加了9辆。
同时,在某个时刻,需要查询在一段连续的道路上,共有多少辆车
查询格式如下:
Q 3 10
Q表明收到的是查询,3是起点,10是终点(包括3和10两处)
要求编程实现正确处理上述信息处理和查询
输入格式
第一行一个整数N(1<=N<=1,000,000),表示标记范围是1到N
第二行一个整数M(1<=M<=100,000),表示探头信息(或查询)的总数量
此后M行,每行一个探头信息或查询请求
输出格式
每逢遇到查询的时候,输出查询范围内的有多少辆车,占一行,查询结果最大不超过2的63次方
输入样例
10
4
H 5 10
Q 1 10
H 6 20
Q 1 10
输出样例
10
30
提示
开始时,整条路上没有车辆

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
ll d[1000010];
ll n, m;

ll lowbit(ll x)
{
    return x & (-x);
}

void add(ll i, ll num)
{
    while (i <= n)
    {
        d[i] += num;
        i += lowbit(i);
    }
}

ll getnum(ll i)
{
    ll s = 0;
    while (i > 0)
    {
        s += d[i];
        i -= lowbit(i);
    }
    return s;
}
int main()  
{
    cin >> n;
    cin >> m;
    for (int i = 1; i <= n; i++)
    {
        d[i] = 0;
    }
    for (int i = 0; i < m; i++)
    {
        char ch;
        ll s1, s2;
        cin >> ch >> s1 >> s2;
        if (ch == 'H') add(s1, s2);
        if (ch == 'Q') cout << getnum(s2) - getnum(s1 - 1) << endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值