DHUACM-contest1

主人的任务罢了

A. Most Unstable Array

题意
给你一个由n个非负整数构成的数列a,其中元素为:\(a_1,a_2,a_3,...,a_n\),其每个元素的总和为m,求 \(ans=\Sigma_{i=1}^{n-1}|a_i-a_{i+1}|\) 的最小值。
思路
找规律:
简单分析一下,特殊地,\(n=1\)时,最大\(ans=0;n=2\)时,一个为\(0\),另一个为\(m\)时,最大\(ans=m;n=3\)时,你发现最大\(ans\)还是\(2*m\)\(n\)再增大,好像也还是\(2*m\),,嗯,那答案应该就出来了!
代码

#include<iostream>
using namespace std;

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        if(n==1) cout<<0<<endl;
        if(n==2) cout<<m<<endl;
        if(n>=3) cout<<2*m<<endl;
    }
    return 0;
}

B. Two Arrays And Swaps

题意
给你两个元素个数同为\(n\)的数列\(a,b\),让你进行不超过\(k\)次的交换,使得数列\(a\)的和尽可能大,输出这个值。
思路
把数列\(b\)的前\(k\)大的元素塞入数列\(a\)中,然后给\(a\)从大到小排序,取前\(n\)的数的和就好了
代码

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

bool cmp(int x,int y)
{
    return x>y;
}

vector<int> arr1,arr2;

int main()
{
    int t,n,i,k,sum,num,x;
    cin>>t;
    while(t--)
    {
        arr1.clear();
        arr2.clear();
        cin>>n>>k;
        sum=0;
        num=0;
        for(i=0;i<n;++i)
        {
            cin>>x;
            arr1.push_back(x);
        }
        for(i=0;i<n;++i)
        {
            cin>>x;
            arr2.push_back(x);
        }
        sort(arr2.begin(),arr2.end(),cmp);
        for(i=0;i<k;++i)
            arr1.push_back(arr2[i]);
        sort(arr1.begin(),arr1.end(),cmp);
        for(i=0;i<n;++i)
            sum+=arr1[i];
        cout<<sum<<endl;
    }
    return 0;
}

C. Board Moves

题意
给你一个奇数\(n\)边长的矩形,从\((1,1)\)\((n,n)\)矩形的每个位置上都有一个图形,它可以向周围相邻8个位置移动,求矩形每个位置上图形移动到矩形正中间位置需要多少次?(设为\(ans\)
思路
n=1时,矩形所有图形移动到中心需要0次,\(ans=0;n=3\)时,矩形新增加的一层上的图形移动到中心分别需要\(1\)次,共计\(ans=8*1+0次;n=5\)时,新增加的一层上的图形移动到中心分别需要\(2\)次,共计\(ans=16*2+8*1+0\);依次类推,得出总次数的求和公式,再利用平方求和得出公式即可。
代码

#include<iostream>
using namespace std;

int arr[500000];

int main()
{
    long long t,n,m,i,sum;
    cin>>t;
    while(t--)
    {
        cin>>n;
        long long m=(n+1)/2;
        cout<<4*m*(m-1)*(2*m-1)/3<<endl;
    }
    return 0;
}

D - Constructing the Array

题意
设接下来出现的每个数组左端点下标为\(a\),右端点下标为\(b\)。给你一个全都是0组成的数组\(arr\),首先令\(arr[(a+b)/2]=1\),然后再找出这个数组里有这最长连续0的子数组再次令\(arr[(a+b)/2]=2\),然后再找出这个数组里有最长连续\(0\)的子数组...依次类推,每次符的值为当前正在进行操作次数\(k\),一共进行\(n\)次赋值,打印赋值完成后数列。
思路
二分+排序。用二分遍历每次数组的每个元素,记录每个元素的小标\(index\)和所处的连续为\(0\)的子数组的长度\(size0\),记录到类型的结构体数组中,二分完毕按照\(size0\)\(index\)进行\(sort\),之后按照排序依次填数就好了。
代码

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

void find0(int,int);

struct node
{
    int index;
    int size0;
}arr1[300500];

bool cmp(node x,node y)
{
    if(x.size0>y.size0) return 1;
    return x.size0==y.size0?x.index<y.index:0;
}

int arr2[300500],i;
int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        i=1;
        find0(1,n);
        sort(arr1+1,arr1+n+1,cmp);
        for(i=1;i<=n;++i)
        {
            arr2[arr1[i].index]=i;
        }
        for(i=1;i<=n;++i)
            cout<<arr2[i]<<' ';
        cout<<endl;
    }
    return 0;
}

void find0(int a,int b)
{
    if(a>b) return ;
    if((b-a+1)%2!=0)
    {
        arr1[i].size0=b-a+1;
        arr1[i++].index=(b+a)/2;
        find0(a,(b+a)/2-1);
        find0((b+a)/2+1,b);
    }
    else
    {
        arr1[i].size0=b-a+1;
        arr1[i++].index=(b+a-1)/2;
        find0((b+a-1)/2+1,b);
        find0(a,(b+a-1)/2-1);
    }
}

E - K-periodic Garland

题意
给你一个由0,1组成的字符串,每次操作你可以把字符串中的0改为1或者1改为零,求使得字符串中所有1的位置都相差常数k所需要最少操作次数。
思路

  • 最后结果的字符串的1的位置可以对应(k-1)种情况(10循环从位置0开始,从位置1开始,直到从位置k-1开始,因为在位置k出开始就和从0开始存在重复而且可能不完全)。
  • 每种情况位置上的数提取出来分别组成一个新的字符串,此时原来的问题主要就变成了求新字符串中所有出现的1都连续(或者不存在)的最小次数,这个可以用dp解决。
  • 设dp[i]为设新字符串第i个位置为1时,在i之前(含i)的字符串满足条件所需要的最小次数。从dp[0]逐步求到 dp[每组最后一个元素的位置] 。最后取两次最小就好了。
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;

int all_1;

int num(string str);

int main()
{
    int t,n,k;
    string str;
    cin>>t;
    while(t--)
    {
        int ans=1e9;
        cin>>n>>k;
        vector<string> s(k);
        cin>>str;
        all_1=count(str.begin(),str.end(),'1');
        for(int i=0;i<k;++i)    //求(k-1)个子串
            for(int j=i;j<n;j+=k)
                s[i]+=str[j];
        for(int i=0;i<k;++i)
            ans=min(num(s[i]),ans);//在k-1个结果中取最小
        cout<<ans<<endl;
    }
    return 0;
}

int num(string str)
{
    int len=str.size();
    int num_1=0;
    int tot_1=count(str.begin(),str.end(),'1');
    bool flag;
    vector<int> dp(len);
    for(int i=0;i<len;++i)
    {
        str[i]=='1'?flag=0:flag=1;
        num_1+=1-flag;  //记录从0到i出现的‘1’的个数
        dp[i]=flag;    //首先让dp[i]等于把第i个位置上变成1的次数
        if(i>=1)
        {
            dp[i]+=min( dp[i-1] , num_1-(1-flag) );//每次求最小次数要考虑两种情况
            dp[i-1]+=tot_1-num_1+(1-flag);
        }
    }
    int ans=num_1;
    for(int i=0;i<len;++i)
        ans=min(ans,dp[i]);//一种子串最小次数解

    return ans+all_1-num_1;
}

F - Decreasing Heights

难!不会!先摸了!

posted @ 2020-12-06 15:05  七铭的魔法师  阅读(65)  评论(0编辑  收藏  举报