题意:给你一个矩阵(n*m)的。这个矩阵从上到下由1,2,3````n * m填满.
再给你一个数x。问你在另一个矩阵(也是n * m,不过是从水平填起),这个x的位置是什么数.
思路: 算出x的坐标。然后再按矩阵的性质代入求值就行.
代码:
#include
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
long long n,m,x;
cin>>n>>m>>x;long long xx,yy;
if(x%n==0) xx=n; else xx=x%n;
if(x%n==0) yy=x/n; else yy=x/n+1;
long long ans=(xx-1)*m+yy;
cout<<ans<<endl;
}
}
题意:给一个长度为n,且仅有 . 和 * 的字符串。满足下列两条件下,求最少用多少个"x"将“ * ”替换。
条件1: 开头第一个*与最后一个 * 必须替换。
条件2: 两个替换后的 x 之间的距离不超过k。
思路: 贪心即可。从第一个 * 开始 每次都去找一个最大距离的 * 然后替换,直到最后一个 .
#include
using namespace std;
int n,k;string s;
int main(){
int T;
cin>>T;
while(T--){
cin>>n>>k;
cin>>s;
int tot=0;
int begin=0;int end=0;int first=0;
for(int i=0;i<n;i++) {
if(s[i]=='*') tot++,end=i;
if(s[i]=='*'&&first==0) begin=i,first=1;
}
if(tot==1) {
cout<<1<<endl; continue;
}
if(tot==2) {
cout<<2<<endl; continue;
}
int cnt=0;
s[begin]='x';s[end]='x';
for(int i=begin;i<n;){
int lb=min(n-1,i+k);bool flag=false;
if(lb>=end) break;
for(int j=lb;j>i;j--) {
if(s[j]=='*') {
cnt++; flag=true;
i=j;
break;
}
}
}
cout<<cnt+2<<endl;
}
}
题意: 给两个字符串a,b. 有两种操作: 1. 对a去头或者去尾。2. 对b去头或者去尾. 求最少操作使得a,b相同.
思路1: 暴力枚举 直接枚举所有可能长度,然后再枚举i,j位置的字串,复杂度是O(min(n,m)nm)
思路2 : dp 实际上这是求一个最长公共连续子序列。与传统LCS不同的是,我们要求的LCS要是连续的.不难得转移方程.
dp[i+1][j+1]=dp[i][j]+1;(a[i]=a[j])
dp[i+1][j+1]=0(a[i]!=a[j])
代码: (dp解决)
#include
using namespace std;
int dp[55][55];
int main(){
int T;cin>>T;
while(T--){
string a,b;cin>>a>>b;
int n=a.size();int m=b.size();
int ans=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(a[i]==b[j]){
dp[i+1][j+1]=dp[i][j]+1;
}
else dp[i+1][j+1]=0;
ans=max(dp[i+1][j+1],ans);
}
}
cout<<n+m-2*ans<<endl;
}
return 0;}
给一个长度为n的整数序列,选择两个不同的数字从数组中删去。求进行若干次操作后,数组的最小长度.
思路: 贪心。显然是删去次数比较多的好,免得后面都堆一块删不掉了。然后注意用map记录下每个数字的出现情况。因为数字比较大需要离散化,再把map遍历一次录入优先队列中,然后模拟删除即可。
#include
using namespace std;
map<int,int> v;
int main(){
int T;cin>>T;
while(T--){
int n;cin>>n;v.clear();
for(int i=0;i<n;i++) {
int temp;scanf("%d",&temp);
v[temp]++;
}
int ans=0;priority_queue<pair<int,int> > q;
for(auto i : v){
int x1=i.first; int x2=i.second;
q.push(make_pair(x2,x1));
}
while(q.size()>=2){
int x1=q.top().first;int x2=q.top().second;q.pop();
int y1=q.top().first;int y2=q.top().second;q.pop();
x1--;y1--;
if(x1) q.push(make_pair(x1,x2));
if(y1) q.push(make_pair(y1,y2));
ans+=2;
}
cout<<n-ans<<endl;
}
return 0;}
题意: 对于一个n的全排列(只有1~n的数字组成)P. 进行以下操作可以得到一个序列 q . qi=max(p1,p2,p3~~~~~,pi) .显然(题目暗示)一个 P是对应一个 序列q的,但反过来却不是。现在给你一个序列q,要你求可能还原回去的序列P.
输出字典序最大的序列P与字典序序列最小的序列P。
思路: 好家伙,n=2e5,我tm直接开始思考构造.字典序最大的很好构造.一个显然的性质是,qi序列一定是不递减的。每次出现新的qi(与前面不同),代表这个序列增加了“可用”的数字。我们用一个set维护它。每当加入一个新的qi,把(qi-1)+1 到 qi 全部加入set 然后每次都挑选set里面的最大值(set自带有序)。由于贪心的关系,每次我们都会选最大值。所以之前多加的那些数字是不会被选到的。
但是,在构造字典序最小的时候直接暴力地(qi-1)+1 到 qi 全加是不可取的。因为这时候会选了还没“应该出现”的数字 . 比如你第一个是 3 直接把1——3全加,你就会选了1. 这是不科学的.正解是用一个变量now指向当前最小值,开一个vis数组标记当前数字是否用过.然后当qi+1与qi相同时,说明没有出现新的最大值,就把还没出现的最小的now加入set.
由于每个元素只会加入集合一次 大概是O(nlogn)吧
#include
using namespace std;
const int maxn = 2e5+5;
int q[maxn];int ans_min[maxn];int ans_max[maxn];bool vis[maxn];
int main(){
int T;cin>>T;
while(T--){
int n;cin>>n;
for(int i=0;i<n;i++) scanf("%d",&q[i]);
memset(vis,0,sizeof(vis));
set<int> s; int now_max=0;int now=1;
for(int i=0;i<n;i++) {
bool flag=false;int pre=now_max;
if(q[i]>now_max) { flag=true; now_max=q[i];
}
if(flag) {
s.insert(now_max);vis[now_max]=true;
}
else {
while(vis[now]&&now<n) now++;
vis[now]=true;s.insert(now);
}
set<int>::iterator p;
p=s.begin();
ans_min[i]=*p;
s.erase(p);
}
for(int i=0;i<n;i++) {
if(i==0) cout<<ans_min[i];
else cout<<' '<<ans_min[i];
}
cout<<endl;
s.clear();now_max=0;
for(int i=0;i<n;i++) {
bool flag=false;int pre=now_max;
if(q[i]>now_max) { flag=true; now_max=q[i];
}
if(flag) {
for(int j=pre+1;j<=now_max;j++) s.insert(j);
}
set<int>::iterator p;
p=s.end();
ans_max[i]=*(--p);
s.erase(p);
}
for(int i=0;i<n;i++) {
if(i==0) cout<<ans_max[i];
else cout<<' '<<ans_max[i];
}
cout<<endl;
}
return 0;}
题意 给你一个无限二叉树,但是这个二叉树每层的结点就是它的层数.1层有一个结点,2层有两个结点.然后呢每个结点有一个坐标(r,c)r代表这个结点层数,c代表这个结点是这层第c个。现在呢,每个结点有两条路,如果你r+c是个偶数,那么你可以免费通向(r+1,c)(左边),或者花一点cost去(r+1,c+1). r+c是奇数就反过来,免费右走,付费左走。现在呢,在给你n个结点,问你从(1,1)出发跑完这些结点最少花费是多少.(结点数 n=2e5).
思路: 好家伙,二叉树还整那么多结点,根据lrj指导,我直接开始找规律。注意到偶数变奇数不用给钱,而且奇数如果不给钱就一直是奇数.
这点性质非常有用,说明如果我们要花钱,一定是花在了维护一个点是偶数的情况。什么时候要维护一个点是偶数? 没错,就是让它的坐标 C不增加。因为奇数让c+1是不用付费的。记当前结点为(r1,c1) 要去的结点是(r2,c2)
决策次数一共是r2-r1次. 记r3=r2-r1; c3=c2-c1; _even=r3-c3. _even就是一共需要让c停下来的次数. 如果开始r1+c1就是偶数那么我们还能白嫖一次停下来的钱,故 even–; 然后我们发现每次花费1,实际上可以停两次.奇数->偶数->奇数,这两步中,r+2了,但c却仍保持不变.然后统计花费即可. 复杂度 应该非常可观.
另外,还有一个特例,就是题目里面那个1 和10000~~那个。因为没有多余的动作把偶数转换为奇数,所以只能一直花费,记得特判以下。条件就是
(pre_c+pre_r)%2==0&&_even==0
#include
using namespace std;
const int maxn = 2e5+2;
struct Node{
int r,c;
bool operator < (const Node &x) const {
return r<x.r;
}
}a[maxn];
int main(){
int T;cin>>T;
while(T--){
int n; cin>>n;
for(int i=0;i<n;i++) {
scanf("%d",&a[i].r);
}
for(int i=0;i<n;i++) scanf("%d",&a[i].c);
sort(a,a+n);
int pre_r=1;int pre_c=1;long long cost=0;
for(int i=0;i<n;i++){
int delta_c=a[i].c-pre_c;int delta_r=a[i].r-pre_r;
if(delta_r==0) continue;
int _even=delta_r-delta_c;
if((pre_c+pre_r)%2==0&&_even==0) cost+=delta_r;
else {
if((pre_r+pre_c)%2==0) _even--;
if(_even>0) if(_even%2==0) cost+=_even/2; else cost+=(_even/2)+1;
}
pre_c=a[i].c;pre_r=a[i].r;
}
cout<<cost<<endl;
}
return 0;}
题意:给一个字符串(长度n=2e5) s,每次可以进行以下操作,要求得到字符串t字典序最大.
操作:挑选一个字母至少出现过两次的,删去其中任意一个.
最后要求得到一个字符串t,s中有的元素t都有.然后字符串t中的字母是唯一的,是一个集合。
思路:跪了跪了. 参考答案了.删除太复杂了,我们想要挑选,因为挑选才可能出现字典序最大嘛。我们说挑选某个元素 si 那么,他后面一定还有 si.为啥? 你是怎么挑选的,不就是把后面相同的si元素删光然后留下这个吗.
将s字符串加入一个集合s. 每次遍历这个集合找到一个合法的最大字母.把这个字母加入到答案字符串t中。
怎么样的字母才算合法呢? 就是选完这个元素后,剩下的右边那一串的元素种类是m - 1 (m是当前串仍剩余元素的种类). 每次操作完,为了避免重复挑选,还要把右边那一串删去挑选元素作为下一次考量的字符串s.
因为需要位置合法,你不能挑完一个数,再去挑前面的那些,你是去挑后面的那些.
所以需要一个cal函数计算某字符串的元素种类.使用了unique 和sort写.
然后一个构造字符串的函数,构造符合条件的字符串(右边有m-1个元素).
#include
using namespace std;
const int maxn = 2e5+5;
int cal(string s){
sort(s.begin(),s.end());
return unique(s.begin(),s.end())-s.begin();
}
string _build(string s,char ch){
string t;bool first=false;
int n=s.size();
for(int i=0;i<n;i++){
if(s[i]!=ch&&first) t+=s[i];
else if(s[i]==ch) first=true;
}
return t;
}
int main(){
int T;cin>>T;
while(T--){
string s;cin>>s;
unordered_set<char> ss;
for( auto p : s) ss.insert(p);
int m=cal(s);string t;
while(m>0) {
char _max=0;
for(auto p : ss) {
if ( cal(_build(s,p)) == m-1 ){
_max=max(_max,p);
}
}
t+=_max; m--;
s=_build(s,_max);
ss.erase(_max);
}
cout<<t<<endl;
}
return 0;}
第一次写… 不好的多包涵