Lonlife-ACM Round #7

A題: 

原題鏈接:Boring Game  (數學思維)


題意:給出一組下標從1到n的數列,對於任意一個i>1的數都可以將(ai-1,ai,ai+1)轉換成 (ai-1 + ai,-ai,ai+1 +ai),再給出一段長度爲n數列b。 問a通過轉換是否能變成b


題解:我們可以把數列中數的轉換看成前綴和轉換。 於是就有(si-1,si,si+1)變成(si,si-1,si+1)。就可以看到,數的轉變實質就是序列的前綴和si與si-1交換了位置。 直接判一下兩個序列的前綴和是否能對等就好了。


代碼如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
typedef long long LL;
LL a[maxn],b[maxn];
int main()
{
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		LL num;
		scanf("%lld",&a[0]);
		for(int i=1;i<n;++i){
			scanf("%lld",&num);
			a[i]=a[i-1]+num;
		}
		scanf("%lld",&b[0]);
		for(int i=1;i<n;++i){
			scanf("%lld",&num);
			b[i]=b[i-1]+num; 
		}
		sort(a,a+n);
		sort(b,b+n);
		if(equal(a,a+n,b)) 
			puts("Yes");
		else	puts("No");
	}
	return 0;
} 



B題,原題鏈接:Capture  (樹的刪邊與深度)


題意:有一個原點爲1,然後把1後面的點按順序連入前面的點中。 假設當前點是i,+3就表示把i連在3後面,接下來+4就表示把i+1連在4後面。 -i就表示將i與3的連接切斷。 有如上n次操作, 輸出每一次操作後距離1最遠的最小點。


題解: 由於題目給出了15s,所以標記距離最遠的最小點Max,然後在每次+x的時候都能O(1)查找, 在-x的時候,由於連接在x之後的點一定大於x,所以直接從1到num點(當前數值最大的點)遍歷一遍將切掉的點,距離改成0,然後跟新Max,這個操作爲O(n),總體複雜度爲O(n*n/2) 。 7000+ms過的題,時限給的大,題目難度沒了。


官方題解中給出了近似O(n*logn)方法,也很簡單。

代碼如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
int tree[maxn];
int deep[maxn];

int main()
{
	int t,n;
	char str[10];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		int k=2;
		memset(deep,0,sizeof(deep));
		deep[1]=1;
		int Max=1;
		for(int i=0;i<=n;++i)
			tree[i]=i;
		while(n--)
		{
			scanf("%s",str);
			int v=0;
			for(int i=1;i<strlen(str);++i)
				v=v*10+(str[i]-'0');
			if(str[0]=='+')
			{
				deep[k]=deep[v]+1;
				tree[k]=v;
				if(deep[k]>deep[Max])
					Max=k;
				printf("%d\n",Max);
				k++;
			}
			else if(str[0]=='-')
			{
				deep[v]=0;
				tree[v]=0;
				Max=1;
				for(int i=1;i<k;++i)
				{
					if(deep[tree[i]]==0)
						deep[i]=0;
					if(deep[i]>deep[Max])
						Max=i;
				}
				printf("%d\n",Max);
			}
		}
	}
	return 0;
} 


C題,原題鏈接: Duplicate Numbers  (map)


題意:輸出序列中至少出現兩次的數。


題解:map,sort都可以隨便搞一搞就O(nlogn)


代碼如下;


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
int maxn = 1e5+10;
int main()
{
	int t,n,a;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		map<int,int> mp;
		for(int i=0;i<n;++i)
		{
			scanf("%d",&a);
			mp[a]++;
		}
		map<int ,int>::iterator it;
		int flag=0;
		for(it=mp.begin();it!=mp.end();++it)
		{
			if(it->second > 1)
			{
				if(flag)
					printf(" %d",it->first);
				else
				{
					flag=1;
					printf("%d",it->first);
				}
			}
		}
		if(!flag)
			printf("none");
		printf("\n");
	}
	return 0;
} 


D題,原題鏈接:Pick Up Coins  (dp)


題意:給出一個下標1到n數列,每次可以在數列去一個數a[i],貢獻 a[i-1]*a[i]*a[i+1]。a[0]=1,a[n+1]=0。 問貢獻之和最大可以爲多少?


題解: 經典的轉態轉移方程啊,太弱了,不會。。。和矩陣鏈乘類似參考博客 


代碼如下:


#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 1e3+10;
int dp[maxn][maxn],a[maxn];//dp[i][j] 表示撿起i~j區間的1硬幣 
int main()
{
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]);
		a[0]=a[n+1]=1;
		for(int i=0;i<=n+1;++i)
			for(int j=0;j<=n+1;++j)
				dp[i][j]=0;
		for(int len=1;len<=n;++len)
			for(int i=1,j=i+len-1;j<=n;++i,++j)//從小區間開始,保證小區間就是最優的 
				for(int k=i;k<=j;++k)
					dp[i][j]=max(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[i-1]*a[k]*a[j+1]);
		printf("%d\n",dp[1][n]);
	}
	return 0;
} 


E題,不想看了。。。想起來就再補

發佈了643 篇原創文章 · 獲贊 225 · 訪問量 50萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章