cf #617 C-E2

t3一直在想双指针,思路太沙雕正解就是用一个map<pair<int,int>,int>记录最后到达当前位置的是第几个字符,想法就是如果现在到达的位置之前也到达过,那么这段就可以删掉。然后使得可以删掉的长度大小最小,并记录对应的posl,posr。然后一些细节也弄了挺久,位置各种+1 -1的错...还是太菜了

#include<bits/stdc++.h>
using namespace std;
const int inf=200000+10;
map<pair<int,int>,int> mp; //***
int main()
{
	string s;
	int t,i,j,k,n,len,f,posl,posr;
	//freopen("617c.txt","r",stdin);
	cin>>t;
	while (t--)
	  {
	  	int min=inf;
	  	pair<int,int> p=make_pair(0,0);//***
	  	cin>>n;mp.clear();
	  	cin>>s;len=s.length();
	  	posl=-1;posr=-1;
		mp[{p.first,p.second}]=0;
	  	for (i=0;i<len;i++)
	  	  {
	  	  	if (s[i]=='U') p.second++;
	  	  	if (s[i]=='D') p.second--;  	  	  	  	
	  	  	if (s[i]=='R') p.first++;
	  	  	if (s[i]=='L') p.first--;
	  	  	if (mp[{p.first,p.second}]!=0||(p.first==0&&p.second==0))
	  	  	  {
	  	  	  	k=i-mp[{p.first,p.second}]+1;
	  	  	  	if (k<min)
	  	  	  	  {
	  	  	  	  	min=k;
	  	  	  	  	posl=mp[{p.first,p.second}]+1;
	  	  	  	  	posr=i+1;
				  }
			  }
			mp[{p.first,p.second}]=i+1;
		  }
		if (posl==-1&&posr==-1) cout<<-1<<endl;
		else cout<<posl<<' '<<posr<<endl;
 	  }
 	//fclose(stdin);
 	return 0;
}

  

D题其实比较水,对于当前的怪物,算最少要使用技能多少次才能拿分,然后排个序就完了

算使用技能次数的时候也弄错了一些小细节......

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200+10;
int h[maxn],cnt[maxn];
int main()
{
    int n,a,b,k,i,j,ans,t,y;
    scanf("%d%d%d%d",&n,&a,&b,&k);
    for (i=1;i<=n;i++) scanf("%d",&h[i]);
    memset(cnt,0,sizeof(cnt));
    for (i=1;i<=n;i++)
      {
        if (h[i]<=a) continue;
        t=h[i]%(a+b);
        if (t>0&&t<=a) continue;
        if (t==0)
          {
            if (b%a==0) cnt[i]=b/a;else cnt[i]=b/a+1;
            continue;
          }
        if (t%a==0) cnt[i]=t/a-1;else cnt[i]=t/a;
      }
    //for (i=1;i<=n;i++) printf("%d ",cnt[i]);
    sort(cnt+1,cnt+n+1);
    i=j=0;
    while (j<=k)
      {
        i++;if (i>n) break;
        j+=cnt[i];
      }
    ans=i-1;printf("%d\n",ans);
    return 0;
}

  

E1和E2

先看E2吧。结合题解有了自己的一点想法。首先找出原序列的最长严格下降子序列,设长度为m,那么就至少需要m种颜色了。如果颜色<=m-1种,这m个数必定有两个数同色,次序一定无法交换,所以最后序列肯定不是单调不降的。那么有m种颜色就可以划分了。因为根据dilworth定理(emmm不太会证,但是知道这个结论),原序列可以划分成m个不下降子序列,然后给每个不下降子序列染成相同的颜色就行了。其实这道题的本质就是最少能划分成多少个不下降子序列

 

有两个有点巧的地方:

一是虽然n<=100000,看上去直接求个lis复杂度不太行,然后nlogn的lis保存方案也好烦。但是每个元素的范围只有1-26,所以可以用pos[i](1<=i<=26)保存i出现的最后一个位置,因为相同的元素,位置越靠后的,这个位置能连的下降子序列就越长。这样时间复杂度只有O(26n)。二是染色的时候只要染成下降子序列dp数组的元素值就行了(这个一开始没想到)。因为dp数组的最大值为m,然后如果元素相同,就一定是单调不降的。这样刚好划分成m个不下降子序列

代码超级短:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200+10;
int dp[maxn],pos[30],a[maxn]; //pos[i]:字符i(1-26)的最后一个位置 
string s;
int i,j,k,t,n;

int main()
{
	std::ios::sync_with_stdio(false);
	cin>>n>>s;
	for (i=0;i<n;i++)
	  a[i+1]=s[i]-'a'+1;
	memset(dp,0,sizeof(dp));
	memset(pos,0,sizeof(pos));
	dp[1]=1;pos[a[1]]=1;
	for (i=2;i<=n;i++){
		dp[i]=1;
		for (j=1;j<=26;j++)
		  if (pos[j]>0&&j>a[i]) dp[i]=max(dp[i],dp[pos[j]]+1);
		pos[a[i]]=i;
	}
	int ans=0;
	for (i=1;i<=n;i++) ans=max(dp[i],ans);
	cout<<ans<<endl;
	for (i=1;i<=n-1;i++) cout<<dp[i]<<' ';
	cout<<dp[n]<<endl;
	return 0;
}

  

然后看E1。E1就是问能不能染成两种颜色,当然可以用E2的方法做,而且E1的n<200,直接求下降子序列就行了。但是题解给出了一个贪心的方法感觉挺神奇的,脑补一下感觉挺对的:设已经有两个子序列,如果一个新的元素>=子序列一的末位,就把它连到一后面,如果>=二的末位就连到二后面,否则无解......

#include<bits/stdc++.h>
using namespace std;
string s,ans;

int main()
{
    int i,j,n,f;
    cin>>n>>s;
    char last1,last2;
    last1='a';last2='a';
    ans=""; f=0;
    for (i=0;i<n;i++)
      {
      	if (s[i]<last1&&s[i]<last2){
      		cout<<"NO"<<endl;f=1;
      		break;
		  }
		if (s[i]>=last1){
			ans+='0';
			last1=s[i];
		}
		else if (s[i]>=last2){
			ans+='1';
			last2=s[i];
		}
	  }
	if (f==0){
		cout<<"YES"<<endl;
	    cout<<ans<<endl;
	}
	return 0;
 } 

  

posted @ 2020-02-09 20:04  coastal_taipan  阅读(202)  评论(0)    收藏  举报