1. 程式人生 > >Codeforces Round #486 (Div. 3)題解

Codeforces Round #486 (Div. 3)題解

這周趁著有Div3想上上分,過了3題,900多名。本以為這下上1400穩了吧,沒想到最後一看Rating,1399.。。。看來想變成青名還得繼續打嘍(希望別再掉下去)。

言歸正傳,我們來看一下這次Div3的題解

A題

這題確實比較水,由於輸出任意一組就可以,那麼就暴力了,使用used陣列標記一下之前出現的元素就可以嘍~

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,k;
	int cnt=0;
	int a[10000];
	int used[10000];
	int res[10000];
	int u=0;
	int flag=0;
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++)
	scanf("%d",&a[i]);
	for(int i=0;i<n;i++)
	{
		if(!used[a[i]])
		{
			cnt++;
			used[a[i]]=1;
			res[u++]=i+1;
		}
		if(cnt==k)
		{
		  flag=1;
		  break;
	    }
	}
	if(flag==0)
	{
		printf("NO\n");
	}
	else
	{
		printf("YES\n");
		printf("%d",res[0]);
		for(int i=1;i<u;i++)
		printf(" %d",res[i]);
		printf("\n");
	}
	return 0;
} 

B題

應該是考字串排序。若前一個能成為後一個的子串,那麼前一個串的長度必定不大於後一個,根據這個首先排個序。

接下來就是字串匹配了。可以暴力(因為串長度只有100),當然神犇也可以用KMP(覺得這道題寫KMP稍微有些浪費時間)。

#include<bits/stdc++.h>
using namespace std;
string a[110];
bool cmp(string x,string y)
{
	return x.size()<y.size();
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	cin >> a[i];
	sort(a,a+n,cmp);
	if(n==1)
	{
	  printf("YES\n");
	  cout << a[0] <<endl;
    }
    else
    {
    	int fff=1;
		for(int i=0;i<n-1;i++)
    	{
			int ff=0;
			for(int j=0;j<=a[i+1].size()-a[i].size();j++)
    		{
    			 int flag=1;
    		     int u=0;
				 for(int k=j;k<j+a[i].size();k++)
    			 {
				      if(a[i][u]!=a[i+1][k])
    			    {
    			 	   flag=0;
    			 	   break;
				    }
				      else
				      u++;
				  }
				  if(flag==1)
				  {
				  	  ff=1;
				  	  break;
				  }
			}
			if(ff==0)
			{
			   fff=0;
			   break;
		    }
		}
		if(fff==1)
		{
			cout << "YES" << endl;
			for(int i=0;i<n;i++)
			cout << a[i] << endl;
		}
		else
		{
			printf("NO\n");
		}
	}
	return 0;
} 

C題

這道題比賽的時候用了很長時間才做出來,歸根結底還是map用的不熟。

思路就是線上處理,每次讀入一列數就把這列數去掉一個元素後可能的情況存入map。我開了兩個map,一個存第幾組,一個存在每組數中的位置,也就是下標。(後來看了網上的程式碼可以用pair存,窩太弱了。。。)每次讀入一組新的數時,就看之前有沒有就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,int> mp1;
map<ll,int> mp2;
ll a[200010];
int main()
{
	int n,r1,r2,r3,r4,t;
	int flag=0;
	scanf("%d",&t);
	for(ll i=1;i<=t;i++)
	{
		scanf("%d",&n);
		ll sum=0;
		for(ll j=1;j<=n;j++)
		{
		  scanf("%lld",&a[j]);
		  sum+=a[j];
	    }
	    if(flag==1)
	    continue;
	    for(ll j=1;j<=n;j++)
	    {
			 map<ll,int>::iterator  it=mp1.find(sum-a[j]);
			 if(it!=mp1.end())
		     {
		     	if(mp1[sum-a[j]]!=i)
		     	{
		           r1=mp1[sum-a[j]];
		           r2=mp2[sum-a[j]];
		           r3=i;
		           r4=j;
				   flag=1;
				   break;
			     }
			 }
			 else
			 {
			 	mp1[sum-a[j]]=i;
			 	mp2[sum-a[j]]=j;
			 }
		}		     
	}
	if(flag==0)
	printf("NO\n");
	else
	{
		printf("YES\n");
		printf("%d %d\n",r1,r2);
		printf("%d %d\n",r3,r4);
	}
	return 0;
} 

D題

可以說是一道數學題,也可以說是思維題

關鍵在於最多答案只有三個數且此時三個數成等差數列且公差為2的冪。(證明過程參考這位博主的部落格)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[200010];
set<ll> s;
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
		s.insert(a[i]);
	}
	ll num=1;
	int u=0;
	ll res[3];
	ll pre[100];
	while(num<=2e9)
	{
		pre[u++]=num;
		num*=2;
	}
	int tot=0;
	int t1,t2;
	for(int i=0;i<n;i++)
	{
		if(tot==2)
		break;
		for(int j=0;j<u;j++)
		{
			int tem=0;
			t1=s.count(a[i]+pre[j]);
			t2=s.count(a[i]+2*pre[j]);
			if(t1&&t2)
			tem=2;
			else
			{
				if(t1||t2)
				{
					tem=1;
				}
				else
				tem=0;
			}
		     if(tem>tot)
		  {
			if(tem==1)
			{
				res[0]=a[i];
				if(t1)
				res[1]=a[i]+pre[j];
				if(t2)
				res[1]=a[i]+2*pre[j];
			}
			else
			{
				res[0]=a[i];
				res[1]=a[i]+pre[j];
				res[2]=a[i]+2*pre[j];
			}
			tot=tem;
		   }
	   }
	}
	if(tot==0)
	{
		printf("1\n%lld\n",a[0]);
	}
	else if(tot==1)
	{
		printf("2\n");
		printf("%lld %lld\n",res[0],res[1]);
	}
	else
	{
		printf("3\n");
		printf("%lld %lld %lld\n",res[0],res[1],res[2]);
	}
	return 0;
} 

E題

最開始想用BFS做,後來感覺過不了。後來看了別人的部落格,發現是貪心。

關鍵在於能被25整除,那麼末尾必是“00”,“25”,“50”。“75”。(這個應該很好理解)

那麼我們就檢查末兩位,如果不是就把前面的數換到後面就好了,最後再對前導0處理一下就ok。詳見程式碼

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
string a;
string b;
int len;
int tot;
void make(char x,char y)
{
	int t1=0,t2=0,f=0;
	if(a[len-1]==y)
	{
		t2=0;
		f++;
	}
	else
	{
		for(int j=len-2;j>=0;j--)
		{
			if(a[j]==y)
			{
			   for(int k=j;k<=len-2;k++)
			   {
			      swap(a[k],a[k+1]);
			      t2++;
			    }
			   f++;
			   break;
		    }
		}
	}
	if(a[len-2]==x)
	{
		t1=0;
		f++;
	}
	else
	{
		for(int j=len-3;j>=0;j--)
		{
			if(a[j]==x)
			{
			   for(int k=j;k<=len-3;k++)
			   {
			      swap(a[k],a[k+1]);
			      t1++;
			    }
			   f++;
			   break;
		    }
		}
	}
	int ind;
	int ff=0;
	if(f==2)
	{
		if(a[0]!='0')
		tot=min(tot,t1+t2);
		else
		{
			for(int i=1;i<=len-3;i++)
			{
				if(a[i]!='0')
				{
					ind=i;
					ff=1;
					break;
				}
			}
			if(ff==1)
			{
				tot=min(tot,t1+t2+ind);
			}
		}
	}
}
int main()
{
	cin >> a;
	b=a;
	len=a.size();
	tot=1000000000;
	if(len==1)
	printf("-1\n");
	else
	{
		 make('0','0');
		 a=b;
		 make('2','5');
		 a=b;
		 make('5','0');
		 a=b;
		 make('7','5');
		 if(tot==1000000000)
		 printf("-1\n");
		 else
		 printf("%d\n",tot);
	}
	return 0;
} 

F題

又是dp題

參考了別人的部落格,這道題有兩種做法

第一種

dp[i][j]表示從0到i時,在點i有第j把傘(沒有傘時j==0)時的最小疲憊值

轉移時,分成三種情況(即對應著三種操作)

dp[i+1][j]=min(dp[i+1][j],dp[i][j]+w[j])  (第i+1個點下雨,同時沒帶傘時不成立,其他條件都成立)  該操作是從第i個點到第i+1個點啥也不做,即第i+1個點的狀態和第i個點的狀態相同

dp[i+1][0]=min(dp[i+1][0],dp[i][j]) (當下一個點沒下雨時成立) 該操作是在第i個點放下傘

dp[i+1][id[i]]=min(dp[i+1][id[i]],dp[i][j]+w[id[i]])  (在第i個點有傘) 該操作是第i個點有傘,拿起傘走到第i+1個點上去

#include<bits/stdc++.h>
#define INF 10000000000
using namespace std;
typedef long long ll;
typedef struct
{ 
	 int ind;
	 int w; 
}  U;
U ubr[2010];
int rain[2010];
int ok[2010];
ll dp[2010][2010];
bool cmp(U x,U y)
{
	if(x.ind!=y.ind)
	return x.ind<y.ind;
	else
	return x.w<y.w;
}
int main()
{
	int a,n,m,t1,t2;
	scanf("%d%d%d",&a,&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&t1,&t2);
		for(int j=t1+1;j<=t2;j++)
		{
			rain[j]=1;
		}
	}
	ubr[0].ind=-1;
	ubr[0].w=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&ubr[i].ind,&ubr[i].w);
	}
	sort(ubr,ubr+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		if(ok[ubr[i].ind]==0)
		ok[ubr[i].ind]=i;
	}
	for(int i=0;i<=a;i++)
	{
		for(int j=0;j<=m;j++)
		{
			dp[i][j]=INF;
		}
	}
	dp[0][0]=0;
	for(int i=0;i<a;i++)
	{
		for(int j=0;j<=m;j++)
		{
			if(j||!rain[i+1])
			dp[i+1][j]=min(dp[i+1][j],dp[i][j]+ubr[j].w);
			if(!rain[i+1])
			dp[i+1][0]=min(dp[i+1][0],dp[i][j]);
			if(ok[i])
			dp[i+1][ok[i]]=min(dp[i+1][ok[i]],dp[i][j]+ubr[ok[i]].w);
		}
	}
	ll ans=INF;
	for(int j=0;j<=m;j++)
	ans=min(ans,dp[a][j]);
	if(ans==INF)
	printf("-1\n");
	else
	printf("%lld\n",ans);
	return 0;
}

第二種

這種我只是看了一下就口胡了,沒有寫。看完之後感覺甚至比第一種好理解?(想看程式碼的話直接看上面的部落格就行)