武汉理工大学第四届ACM校赛传送门
A-ST和TS回文问题
这种题都是有一定简单的规律的
如果每个字符都相等的话,肯定存在
如果字符串T的长度是字符串S的整数倍的话,如果字符串S是回文串或者T的长度是S的长度的奇数倍的话,那么存在
否则不存在
就自己构造字符串来找规律,毕竟这种问是否存在的问题,不可能在那样怎样怎样模拟,肯定是有规律的
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
string s;
int n,q;
//判断是否回文
bool check1(){
int len=s.size();
for(int i=0,j=len-1;i<=j;i++,j--){
if(s[i]!=s[j]) return false;
}
return true;
}
//判断是否全部字符相等
bool check2(){
char ch=s[0];
for(int i=0;i<s.size();i++){
if(s[i]!=ch) return false;
}
return true;
}
void solve()
{
cin>>n>>q;
cin>>s;
while(q--){
int op;
cin>>op;
bool flag=false;
if(op==1){
char ch;
cin>>ch;
s+=ch;
n++;
}
else if(op==2){
int k;
cin>>k;
if(check2()) flag=true;
else if(k%n==0&&(check1()||(k/n)%2==1)) flag=true;
if(flag) puts("Yes");
else puts("No");
}
}
}
signed main()
{
solve();
return 0;
}
B-不降序列
AC代码:
首先要使得权值最大,那么最多操作k次则删除k个数
从删除的反面来看,则是保留,即取子序列嘛
类似于最长上升子序列(动态规划)
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N=510;
int a[N];
int f[N][N];//f[i][j]表示以i个元素结尾,保留j个元素所能获得的最大权值
int sqr(int x){
return x*x;
}
int res;
void solve()
{
int n,m;
cin>>n>>m;
m=n-m;//至少保留m个数
for(int i=1;i<=n;i++) cin>>a[i];
memset(f,-0x3f,sizeof f);
f[1][1]=0;
for(int i=2;i<=n;i++){
for(int j=2;j<=min(i,m);j++){
for(int k=1;k<i;k++){
f[i][j]=max(f[i][j],f[k][j-1]+sqr(a[i]-a[k]));
}
}
if(i>=m) res=max(res,f[i][m]);//取m是因为最少保留m个,保留的越少则权值越大
}
cout<<res<<endl;
}
signed main()
{
solve();
return 0;
}
分析为什么要初始化f为负无穷:
如图,f[1][2]是不合法的,因为以第1个元素为结尾,保留2个元素是不存在的,而如果初始化为0的话,n那么就会转移错误
而初始化f[1][1]为0则是因为它是合法的
E-copy
题目都理解错了,题目的意思是一共有n条信息在剪贴板上,用于粘贴,然后每次问粘贴该n条信息的哪一行到文本中,但是呢,每粘贴一次,剪贴板上的n条信息的顺序是会变的,即被粘贴的那一行会跑到第一行,最终要保证文本中的内容与给定文件文本内容一致
用容器vector,方便元素的删除与插入
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
void solve()
{
int n;
cin>>n;
vector<string>a(n+1);
for(int i=0;i<n;i++) cin>>a[i];
int m;
cin>>m;
cout<<m<<endl;
while(m--){
string s;
cin>>s;
for(int i=0;i<n;i++){
if(s==a[i]){
cout<<i+1<<endl;
a.erase(a.begin()+i);
a.insert(a.begin(),s);
break;
}
}
}
}
signed main()
{
solve();
return 0;
}
F-三角形重心
注意make_pair后面是()而不是<>
利用map,关键字为pair,表示三个点横坐标之和和纵坐标之和,值为vector容器,来存放三个点的标号
如果将重心坐标的3倍(未方便,不除以3)放入map中,值为三个点的标号,如果重心已经出现过了,那么就找到了,输出YES,并把两对3个点标号输出,否则输出NO
不知道为什么三重循环不会超时,可能是中间剪枝,只要找到就结束程序,我猜答案是NO的数据都给的很小
事实证明是这样的,x一共有2000个,y一共有2000个,所以最多有2000*2000=4e6个点,然后每一组三个点的标号i,j,k代表一个重心,只要重心的个数超过了4e6,那么就一定会有重复的重心,就可以结束程序,所以时间复杂度在4e6内,不会超时
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#define int long long
using namespace std;
const int N=1e5+10;
int x[N],y[N];
void solve()
{
int n;
cin>>n;
map<pair<int,int>,vector<int>>mp;
for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int k=j+1;k<=n;k++){
if(mp.count(make_pair(x[i]+x[j]+x[k],y[i]+y[j]+y[k]))){
puts("YES");
for(auto v:mp[make_pair(x[i]+x[j]+x[k],y[i]+y[j]+y[k])]) cout<<v<<" ";
cout<<endl;
cout<<i<<" "<<j<<" "<<k<<" "<<endl;
return;
}
else mp[make_pair(x[i]+x[j]+x[k],y[i]+y[j]+y[k])]={i,j,k};
}
}
}
puts("NO");
}
signed main()
{
solve();
return 0;
}
H-小e的果树
贪心,先创建一个结构体,按照树的坐标从小到大排序
遍历每棵树
对于每棵树,都求出它以及它前面的所有树所能摘到的最大果子树,具体操作是首先横轴移动的时间是确定的,然后将该树以及前面所有树的果子按照高度从小到大排序,从高度低的开始摘,一直摘到时间用完
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#define int long long
using namespace std;
const int N=110;
struct node{
int x;
vector<int>a;
bool operator<(const node &W)const{
return x<W.x;
}
}a[N];
int res;
void solve()
{
int n,t;
cin>>n>>t;
multiset<int>s;
int c;
for(int i=1;i<=n;i++){
cin>>a[i].x>>c;
for(int j=1;j<=c;j++){
int h;
cin>>h;
a[i].a.push_back(h);
}
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
for(auto h:a[i].a) s.insert(h);
int timeleft=t-a[i].x,num=0;
for(auto h:s){
if(h<=timeleft){
num++;
timeleft-=h;
}
else break;
}
res=max(res,num);
}
cout<<res<<endl;
}
signed main()
{
solve();
return 0;
}
K-雇佣农民
先利用贪心求得天数(即每天都最大限度地雇佣农民,直到超出了n块矿石,当然如果刚好等于就不需要考虑以下情况了),这样得到的天数是每天最大限度地雇佣农民所得到的最少天数
那为什么就是这个天数呢?因为这样不能刚好得到n块矿石,所以每天不能最大限度雇佣农民,应消减一些,而贪心算出的矿石数是多于n的,所以前面削减以下,调整,可得到刚好n块矿石
那么为什么不取贪心得到的天数再多几天呢?因为题目说如果有多种方案输出在越早的时间雇佣农民尽量多的方案,即天数少的
对于得到的天数,确定每天雇佣的农民数量,每次都取还需要的矿石数/还剩的天数(其中sum每次求的是已雇佣的所有农民所生成的矿石数)和每天限制最大雇佣数的最小值
只有这样,最终才会刚好等于n
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
const int N=2e5+10;
int day,farmer,sum;
void solve()
{
int n;
cin>>n;
while(sum<n){
day++;
farmer+=day;
sum+=farmer;
}
cout<<day<<endl;
vector<int>a(N);
sum=0;
for(int i=1;i<=day;i++){
a[i]=min((n-sum)/(day-i+1),i);
sum+=a[i]*(day-i+1);
}
if(sum==n){
for(int i=1;i<=day;i++) cout<<a[i]<<" ";
}
else cout<<-1<<endl;
}
signed main()
{
solve();
return 0;
}