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題,不想看了。。。想起來就再補