ACM基本算法类题目

糖果传递(排序、中位数、环形均分纸牌,牛客)

题解:
1、关于模型:环状均分纸牌

显然最后每个人都剩下sum/n张纸牌,p[i]表示这个人给下一个人多少张纸牌

显然p[i]=a[i]+p[i-1]-sum/n

p[i]-p[i-1]=a[i]-sum/n,所以p[i]-p[i-1]+p[i-1]-p[i-2]+…-p[1] = sigma(i)(a[i]-sum/n)

即p[i]=sigma(i)(a[i]-sum/n)+p[1]

显然sigma(i)(a[i]-sum/n)是定值,所以p[1]是所有sigma(i)(a[i]-sum/n)的中位数就好了

……

2、我们用xi表示第i个小朋友给第i−1个小朋友的糖果数,其中x1表示第1个小朋友给第n个朋友的糖果数,那么最终答案即为|x1|+|x2|+…|xn|。
我们假设最后每个人剩avg个糖果,那么可以得到:
对于第一个小朋友:a1+x2−x1=avg
对于第二个小朋友:a2+x3−x2=avg

对于最后一个小朋友:an+x1−xn=avg
整理一下即可得到:
x2=avg−a1+x1
x3=avg−a2+x2=2avg+x1−a2−a1

xn=avg−an−1+xn−1=(n−1)avg+x1−∑n−1i=1ai
我们设c[1]=a[1]-ave
c[2]=c[1]+a[2]-ave
则有c[i]=c[i-1]+a[i]-ave
上述式子即可转化为求解|x1|+|x1−c1|+|x1−c2|…+|x1−cn−1|的最小值,那么直接令x1等于c的中位数即可。
3、货仓选址:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数,证明(随便找一个点,若是左边的点比右边多,那么往左移距离和就会减少,反之右移距离减少,一定是中位数最优)。
4、数据范围有毒。
1、至少1e6
2、记得开long long

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000010;
int a[N],c[N];
int main(){
    int n;
	cin>>n;
	ll ans=0;
	ll avg=0;
	for(int i=0;i<n;i++){
		cin>>a[i];
		avg+=a[i];
	} 
	avg/=n;
	for(int i=1;i<n;i++)
		c[i]=c[i-1]+a[i]-avg;
	
	sort(c,c+n);
	/*到这里就是一个货仓选址问题。 */
 	for(int i=0;i<n;i++){
	ans+=abs(c[i]-c[n/2]);
}
printf("%lld\n",ans);
    return 0;
}

城市扩建(无限包含自身的分形,牛客)

/*
std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。
例如std::pair<int,float> 或者 std::pair<double,double>等。
pair实质上是一个结构体,其主要的两个成员变量是first和second,这两个变量可以直接使用。
初始化一个pair可以使用构造函数,也可以使用std::make_pair函数,make_pair函数的定义如下:
template pair make_pair(T1 a, T2 b) { return pair(a, b); }
    一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象。 
另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。但是这样会出现如下问题:
	例如有如下两个定义:
		std::pair<int, float>(1, 1.1);
		std::make_pair(1, 1.1);
其中第一个的second变量是float类型,而make_pair函数会将second变量都转换成double类型。
这个问题在编程是需要引起注意。

*/

#include<bits/stdc++.h>
#include<iostream>
#define ll long long
#define pll pair<ll,ll> 
using namespace std;

pll calc(ll n,ll m){
	if(n==0)return make_pair(0,0);
	ll len=1ll<<(n-1),cnt=1ll<<(2*n-2);
	auto pos=calc(n-1,m%cnt);
	auto x=pos.first,y=pos.second;
	auto z=m/cnt;
	if(z==0)return make_pair(y,x);
	if(z==1)return make_pair(x,y+len);
	if(z==2)return make_pair(x+len,y+len);
	if(z==3)return make_pair(2*len-y-1,len-x-1); 
}
int main(){
	int t;
	cin>>t;
	while(t--){
		ll n,a,b;
		cin>>n>>a>>b;
		auto ac=calc(n,a-1);
		auto bc=calc(n,b-1);
		double x=ac.first-bc.first,y=ac.second-bc.second;
		printf("%.0lf\n", sqrt(x * x + y * y) * 10);
	}
	return 0;
}

round corridor(公约数、思考题,CF)

题目http://codeforces.com/contest/1200/problem/C

两层转盘分格数的最大公约数n,是它们的分隔数,未分隔开的两层转盘可达。可求未分隔开的两层转盘各自的分块数nn(x/n),只需根据x坐标判断内层还是外层,据y坐标除以其所在转盘的分块数mm,得到编号(<mm)。比较内外转盘的编号是否相等即可。

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
 
using namespace std;
 
#define ll long long
 
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
 
int main()
{
    ll n,m,t;
    scanf("%lld%lld%lld",&n,&m,&t);
    ll g=gcd(n,m);
    ll nn=n/g;
    ll mm=m/g;
    for(int i=0;i<t;i++)
    {
        ll a,b,c,d,nb=0,nd=0;
        scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        if(a==1) nb=(b-1)/nn;
        if(a==2) nb=(b-1)/mm;
        if(c==1) nd=(d-1)/nn;
        if(c==2) nd=(d-1)/mm;
        if(nb==nd)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

Compress Words

(暴力单词去重,CF)

题目:http://codeforces.com/contest/1200/problem/E

暴力方法,运行998ms,差一点点就T了。会有更简洁的方法的。

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
 
using namespace std;
 
string a,ans,t;
 
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    cin>>ans;
    for(int i=1;i<n;i++)
    {
        int len=(int)ans.size();
        cin>>a;
        while(!a.empty())
        {
            //cout<<a<<' '<<t<<' '<<ans<<endl;
            if(~ int(ans.find(a,len-a.size())))
            {
                break;
            }
            else
            {
                t.push_back(a.back());
                a.pop_back();
            }
        }
        while(!t.empty())
        {
            ans.push_back(t.back());
            t.pop_back();
        }
        //cout<<ans<<endl;
    }
    cout<<ans<<endl;
    return 0;
}

(巧妙单词去重,CF)

看了别人的题解,好巧妙…不过还是暴力好理解

#include<bits/stdc++.h>
using namespace std;
int net[1000000];
string s;
string ans;
void get_next(int len,string w)
{
    net[0]=-1;
    int i=0,j=-1;
    while(i<len)
    {
        if(j==-1||w[i]==w[j])
        {
            i++;
            j++;
            net[i]=j;
        }
        else
            j=net[j];
    }
}//net[len]时重复的字母数 
int n;
int main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(NULL);
    cin>>n;
    cin>>ans;
    int cnt=ans.length();
    for(int i=2;i<=n;i++)
    {
        cin>>s;
        int tt=s.length();
        cnt=min(cnt,tt);//只需要拿出ans中跟输入的长度相等的字串比较即可 
        s+="#";
        //cout<<cnt<<'@'<<endl;
        s+=ans.substr(ans.length()-cnt);//s加上了同等长度的ans中的字串,中间#隔开,s=s#ans
        //cout<<s<<'$'<<endl;
        get_next(s.length(),s );     
        int ttt=min(net[s.length()],tt);
        //cout<<net[s.length()]<<'^'<<tt<<endl;
        ans+=s.substr(ttt,tt-ttt);//ttt是两个单词重复的数量,此步将重复的去掉,以更新ans 
        //cout<<ans<<'%'<<endl;
        cnt=ans.length();
    }
    cout<<ans<<endl;
    return 0;
}

To The Max(矩阵前缀和,CF)

巧妙处理矩阵,for循环看的我有点晕。。
题目:https://ac.nowcoder.com/acm/contest/1004/K

/*a[i][j]代表了前i行的前j列的所有元素的和,注意DP题目数组下标从1开始,以避免一些麻烦.
*/

#include<bits/stdc++.h>
using namespace std;
int a[105][105];
int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(NULL);	
	int n,i,j,k,t,sum,max;
	while(cin>>n){
		memset(a,0,sizeof(a));
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++){
				cin>>t;
				a[i][j]=a[i-1][j]+t;//输入的数加上上一行同列的数 ,列的前缀和 
				//cout<<a[i][j]<<"#";
			}
		}
	max=0;
	for(i=1;i<=n;i++){//第一行 
		for(j=i;j<=n;j++){//第二行 
			sum=0;
			for(k=1;k<=n;k++){
				t=a[j][k]-a[i-1][k];//前j行前k列的和-前i-1行前k列的和
				sum+=t;
				if(sum<0)sum=0;
				if(sum>max)max=sum;
				cout<<sum<<"*";
				cout<<"   ";
			}
		}
	}
	cout<<max<<endl;
	}
	return 0;
}

Go to School【数组排序并存下标】

https://vjudge.net/contest/341088#problem/D

/*对数组升序排序并输出其下标,使用结构体*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
struct node{
    int a;
    int b;
};
bool cmp(struct node m, struct node n)    //结构体排序
{
    return m.a<n.a;
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(NULL);
    std::cout.tie(NULL);
    int n,m;
    node node[N];
    int ans=0;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>m;
        node[i].a=m;
        node[i].b=i+1;
    }
    sort(node,node+n,cmp);
for(int i=0;i<n;i++){
  cout<<node[i].b<<" ";
 }
 cout<<endl;
 return 0;
}

Disjoint Set of Common Divisors【找出A\B互质公因数】

https://vjudge.net/contest/341088#problem/E

/*我们知道对gcd(a,b)进行因数分解就能得到a,b的所有因数。但是这里需要互质的因数,所以我们这里需要对gcd(a,b)作质因数分解。
质因数分解的代码:
if(g%i!=0)ans++;    //得到一个因数后,我们ans++
while(g%i==0)g/=i //后面有这句话
if(g>2)ans++    //最后还有一个质因子的话需要再++

*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn =1e5+10;
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}//最大公约数
int main()
{
    long long x,y;
    cin >> x >> y;
    long long g=gcd(x,y);
    long long ans=0;
    for(long long i=2;i<=sqrt(g); i++) //对gcd(a,b)作质因数分解
    {
        if(g%i==0)
        {
            ans++;
            while(g%i==0)g/=i;
        }
    }
    if(g>2) ans++;
    cout << ans +1 <<endl;
    return 0;
}
发布了54 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43629813/article/details/99303401
今日推荐