Google Kick Start 2020 --Round E
Longest Arithmetic
题意
给一个长为n的数组,求其中最长的等差子串长度。
思路
从2开始遍历,维护上一个差,然后判断当前i和i-1的差是否相等。同时记录答案
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;
int a[MAX];
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
int n,res=2;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int d=a[1]-a[0],cur=2;
for(int i=2;i<n;i++)
{
if(a[i]-a[i-1]==d)cur++;
else cur=2,d=a[i]-a[i-1];
res=max(res,cur);
}
printf("Case #%d: %d\n",++cas,res);
}
}
Toys
题意
有n个玩具,编号从1-n,每个玩具有玩耍时间E和冷却时间R,i号玩具可以玩Ei时间,然后回进入长Ri的冷却时间。现在从1开始,循环玩各个玩具(n完了继续玩1),同时可以丢掉任意数量的玩具,求最少丢多少玩具能无限玩下去,如果不能无限玩下去,则给出最少丢多少玩具能得到最长游玩时间。
思路
记所有玩耍时间之和为sum,可以发现如下结论:
- 对于某个玩具i,当且仅当Ri<=sum-Ei时,即Ri+Ei<=sum,才能在第二次轮到i时继续玩下去(即将其他所有玩具玩一遍的时间大于等于i的冷却时间)
因为玩玩具是按顺序的,所以可以动态维护当前剩余的玩具的cur_sum,从1-n动态加入i号玩具,每次加入后更新cur_sum,然后贪心的从大到小的删除所有大于cur_sum的Ri+Ei,并且每次删除后都记录当前最多能玩多久。如果最后所有玩具都被删除,那说明不能永久玩下去,输出最长游玩时间。如果最后剩余一些玩具,那么说明可以永久玩下去,输出删除玩具的个数。
其实不贪心也可以,只要删除所有Ri+Ei>cur_sum就可以,不过用优先队列可以自动排序不用遍历,比较方便。
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int MAX=1e5+5;
int a[MAX],b[MAX];
struct cmp{bool operator()(int x,int y){return a[x]+b[x]<a[y]+b[y];}};
priority_queue<int,vector<int>,cmp> Q;
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
while(!Q.empty())Q.pop();
int n,totdel=0,maxxdel=0;
ll sum=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d",&a[i],&b[i]),sum+=a[i];
ll cursum=sum,maxx=sum,curtime=sum;
for(int i=0;i<n;i++)
{
Q.push(i);
curtime+=a[i];
while(!Q.empty())
{
int x=Q.top();
if(a[x]+b[x]<=cursum)break;
cursum-=a[x];
curtime-=a[x]*2;
Q.pop();
totdel++;
}
if(curtime>maxx)
{
maxx=curtime;
maxxdel=totdel;
}
}
if(totdel==n)
printf("Case #%d: %lld %d\n",++cas,maxx,maxxdel);
else
printf("Case #%d: INDEFINITELY %d\n",++cas,totdel);
}
}
Golden Stone
题意
有s种石头,编号从1-s,编号1的石头是黄金石头。然后给一张无向图,图中部分节点可以免费获取某种编号的石头,可以将某一块石头花费1的代价从一个节点转移到另一个节点。并且存在某些可以在任何节点无代价在合成石头的配方,如2 3 4可以合成1。求获得一块黄金石头的最小代价。
思路
很容易可以想到dp,设状态dp[i][j]表示在节点i获得一块编号为j的石头的代价。可以在图上bellman-ford跑一遍,推出dp[i][j]。但是还有配方没有考虑。实际上配方可以在bellman-ford跑的过程中一并维护。每次从队列中读取到一个节点u,可以遍历配方,查看是否可以用配方更新状态,如果可以更新,就再次把u放入队列中。这样对每个节点 u,最多因为配方更新入队两次,因此时间复杂度为在图上bellman-ford跑一遍的两倍。
也可以用分层图求最短路的模型求解,但是要麻烦许多。
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int MAX=305;
const ll INF=1e12;
vector<int> G[MAX];
vector<vector<int> >rcp[MAX];
ll dp[MAX][MAX];
bool inque[MAX];
int n,m,s,r;
void bfs()
{
queue<int>Q;
for(int i=1;i<=n;i++)
for(int j=1;j<=s;j++)
if(dp[i][j]==0){inque[i]=1,Q.push(i);break;}
while(!Q.empty())
{
int u=Q.front();
Q.pop();
inque[u]=0;
for(int i=1;i<=s;i++)
for(int j=0;j<rcp[i].size();j++)
{
ll sum=0;
for(int k=0;k<rcp[i][j].size();k++)
sum+=dp[u][rcp[i][j][k]];
if(sum<dp[u][i])dp[u][i]=sum,inque[u]=1,Q.push(u);
}
int len=G[u].size();
for(int i=0;i<len;i++)
{
int v=G[u][i];
for(int j=1;j<=s;j++)
{
if(dp[v][j]>dp[u][j]+1)
{
dp[v][j]=dp[u][j]+1;
if(!inque[v]) inque[v]=1,Q.push(v);
}
}
}
}
}
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&s,&r);
for(int i=1;i<=n;i++)
{ inque[i]=0;
G[i].clear();
for(int j=1;j<=s;j++)
dp[i][j]=INF,rcp[j].clear();
}
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
int k;
scanf("%d",&k);
for(int j=0;j<k;j++)
{
int x;
scanf("%d",&x);
dp[i][x]=0;
}
}
for(int i=0;i<r;i++)
{
int k;
scanf("%d",&k);
vector<int>rr;
rr.clear();
for(int j=0;j<k;j++)
{
int x;
scanf("%d",&x);
rr.push_back(x);
}
scanf("%d",&k);
rcp[k].push_back(rr);
}
bfs();
ll res=INF;
for(int i=1;i<=n;i++)
res=min(res,dp[i][1]);
if(res==INF)res=-1;
printf("Case #%d: %lld\n",++cas,res);
}
}