目录
A - Casimir’s String Solitaire(思维+水题)
D - Productive Meeting(贪心+思维+优先队列)
E1. Permutation Minimization by Deque(双端队列+水题)
E2. Array Optimization by Deque(贪心+离散化+树状数组)
A - Casimir’s String Solitaire(思维+水题)
题意:
给出一个仅有字母A、B、C组成的字符串。A和B可抵消,B和C可抵消。
问是否可以得到空的字符串。
思路:
统计A,B,C字符的数量,判断B是否等于A+C
AC代码:略
B - Shifting Sort (暴力)
题意:
给定一个长度为n的数字序列,通过规则把序列变得有序,输出操作次数与每次的操作方式,不需要求最小的操作次数。规则:选择区间[l,r],将其中的数都左移d个单位。
思路:
数组有序即最后每个数字都有自己该在的位置,遍历n个位置,每次都将i位置的正确数找到。在区间i~n中选择最小的放在i位置上,下次再在区间i+1~n中找最小,确定i+1位置上的数。每次的偏移量d=正确数的下标-i(查询最小值区间的起始下标)。
WA的原因:固定每次偏移量为1,一步一步的移动,移动次数太多了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=1e5+10;
vector<int>a;
int c[60];
struct node
{
int l,r,d;
}w[1000];
int main()
{
int t,n,x;
cin>>t;
while(t--)
{
cin>>n;
a.clear();
for(int i=1;i<=n;i++)
{
cin>>x;
a.push_back(x);
}
for(int i=0;i<n;i++)
{
w[i].l=0;
w[i].r=0;
w[i].d=0;
}
// sort(c+1,c+1+n);
int p=0;
int cnt=0;
int id=0;
int k=0;
int tt;
while(p<n)
{
int minn=INT_MAX;
for(int i=p;i<n;i++)
{
if(minn>a[i])
{
minn=a[i];
id=i;
}
}
if(id>p)
{
w[k].l=p+1;
w[k].r=id+1;
w[k].d=id-p;
tt=a[id];
a.erase(a.begin()+id);
a.insert(a.begin()+p,tt);
k++;
p++;
}
else
{
p++;
//continue;
}
}
cout<<k<<endl;
for(int i=0;i<k;i++)
{
cout<<w[i].l<<" "<<w[i].r<<" "<<w[i].d<<endl;
}
}
}
C - Ticks (暴力+思维)
题意:
给出一个n*m的字符矩阵,判断该矩阵的*是否都用于构成至少为k的V形。
在第二个样例中,可以由大小为2的V和大小为3的V组成。
思路:
暴力,对于每个*让它作为V型的底端判断它的d是否大于k,如果大于则将该V型的每个点标记,最后判断被标记的点是否等于*的总数。
AC代码:
#include<bits/stdc++.h>
using namespace std;
char a[30][30];
int vis[30][30];
int t,n,m,k;
void check(int x,int y)
{
int d=0,c=0;
for(int i=x-1;i>=1;i--)
{
c++;
if((y-c)<0||(y+c)>m)
break;
if(a[i][y-c]=='*'&&a[i][y+c]=='*')
{
d++;
//continue;
}
else
{
break;
}
}
if(d>=k)
{
vis[x][y]=1;
for(int i=1;i<=d;i++)
{
vis[x-i][y+i]=1;
vis[x-i][y-i]=1;
}
}
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m>>k;
int sum=0;
int s=0;
bool f;
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
cin>>a[i][j];
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(a[i][j]=='*')
{
sum+=1;
check(i,j);
}
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(vis[i][j]==1)
{
s++;
}
}
}
if(sum==s)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}
D - Productive Meeting(贪心+思维+优先队列)
题意:
给定有n个数,每次选择两个不同的数,对它们各减一,使得你减的操作次数尽可能的多,输出操作的方案数及其方案。
思路:
一个优先队列,大根堆,每次从堆顶取两个元素,最大值和次大值,各自减1后再放回队列。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=2e5+10;
priority_queue<pair<int,int>> q;
pair<int,int>a[maxx];
pair<int,int>p[maxx];
int main()
{
int t,n;
cin>>t;
while(t--)
{
while(!q.empty())
q.pop();
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].first;
a[i].second=i;
if(a[i].first>0)
{
q.push(a[i]);
}
}
int c=0;
pair<int,int>p1,p2;
while(q.size()>1)
{
p1=q.top();q.pop();
p2=q.top();q.pop();
p[c].first=p1.second;
p[c].second=p2.second;
c++;
if(--p1.first)
q.push(make_pair(p1.first,p1.second));
if(--p2.first)
q.push(make_pair(p2.first,p2.second));
}
cout<<c<<endl;
for(int i=0;i<c;i++)
{
if(p[i].first>p[i].second)
swap(p[i].first,p[i].second);
cout<<p[i].first<<" "<<p[i].second<<endl;
}
}
}
E1. Permutation Minimization by Deque(双端队列+水题)
题意:
给一个长度为n的序列A,现有一个空的双端队列B,将A中的每个元素依次(1到n)放进B中。B的第一个元素不大于A[i],把A[i]放在B的第一个位置,否则放在末尾。输出序列B。
思路:
双端队列+模拟,vector模拟会超时。
AC代码:略
E2. Array Optimization by Deque(贪心+离散化+树状数组)
题意:
给一个长度为n的序列A,现有一个空的双端队列B,将A中的每个元素依次(1到n)放进B中。
即把A[i]放在B的第一个位置或者末尾,求逆序对最少的序列B的逆序对个数是多少。
逆序对:若i<j,则b[i]>b[j]。
思路:
局部最优能够保证全局最优,每次插入元素时在当前B基础上考虑插在前面还是后面,选择插入后逆序对少的位置。例:当前序列B【2,5,3,4】,此时的逆序对有【5,3】【5,4】;
如果要插入的元素是3,插入前面【3,2,5,3,4】此时的逆序对【3,2】【5,3】【5,4】;
将x插入后面时【2,5,3,4,3】此时的逆序对有【5,3】【5,4】【5,3】【4,3】。
数据进行离散化,求逆序对只需要知道插入的元素比当前序列B中大的有几个,即只需要知道他们之间的相对关系,且a[i]的范围比较大,(1e-9~1e9)所以可以离散化。
数据离散化模板:
for(int i=1;i<=n;i++)
{
cin>>a[i];//原数组
b.push_back(a[i]);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());//去重
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
}//求出去重后从小到大的排名b[i]并赋值给a[i]
树状数组动态维护(单点更新查询模板题)
当把a[i]放在前面时:sum(a[i]-1):查询排名小于a[i]-1的个数
当把a[i]放在后面时:sum(n)-sum(a[i]):大于a[i]的个数===总个数-小于a[i]的个数
for(int i=1;i<=n;i++)
{
tmp=a[i];
add(tmp,1);
p1=sum(tmp-1);//将a[i]加在前面
p2=sum(n)-sum(tmp);//将a[i]加在后面
ans+=min(p1,p2);
}
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=2e5+50;
ll n;
ll a[maxx];
ll tr[maxx];
ll lowbit(ll x)
{
return x&-x;
}
void add(ll x,ll c)
{
while(x<=n)
{
tr[x]+=c;
x+=lowbit(x);
}
}
ll sum(ll x)
{
ll res=0 ;
while(x>=1)
{
res+=tr[x];
x-=lowbit(x);
}
return res;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
vector<ll>b;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b.push_back(a[i]);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
}
memset(tr,0,sizeof(tr));
ll ans=0;
ll tmp,p1,p2;
for(int i=1;i<=n;i++)
{
tmp=a[i];
add(tmp,1);
p1=sum(tmp-1);//加在前面
p2=sum(n)-sum(tmp);//加在后面
ans+=min(p1,p2);
}
cout<<ans<<endl;
}
return 0;
}