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
难!不会!先摸了!