小郭同学老和我说写博客多么多么好,近期又发现了ronnoc的博客,忽然发现如果把大学做ACM的经历都记下来,也是挺好的一件事,说不定有一天我的博客也能帮助到刚刚接触ACM的新人。有朝一日如果我也能变成神牛,回头看看自己成长的过程也是挺好的一件事。加上今天一下午一直在一个题上TLE,最后终于调通了,所以我愉快的决定从今天起开始写博客。既然是计算机方面的,那必须是CSDN啊,果断开通之。
那第一篇就记一下我今天被卡了一下午TLE的题目吧。
CF 283B http://codeforces.com/problemset/problem/283/B Cow Program
题意不解释,相信特意搜这些关键词的人都是看过题的。
题目怎么解???
最傻叉的办法,就是模拟,显然会超时。我们对题目中数据范围要特别注意,n在20左右的一般是2^n复杂度,n如果是1000左右,那估计就是n^2的复杂度,如果出现100000,那么估计是O(n)或者O(nlogn)。
回到这个题,很明显只有第一个数是变的,那么后面不变的数,就可以直接把他们的结果求出来了,即如果在i这一点,需要操作是+(或者是-),那么这一状态走到结束需要的费用是多少(就是y要加多少),或者说这个状态一直会循环。一共2*(n-1)个状态,第一点为减的状态不会出现,自己想一想就知道了。那么就一次对各个点dfs,遇到搜过的点直接返回结果,复杂度O(n).
于是乎我的代码产生了
#include "iostream"
#include "cstdio"
#include "cstring"
#include "cmath"
#include "stdlib.h"
#include "algorithm"
using namespace std;
long long ans[200010][2],a[200010];
bool visit[200010][2];
int T;
long long dfs(int m,int n)
{
if(visit[m][n]==true)
return -1;
visit[m][n]=true;
long long temp;
if(n==0)
{
if(a[m]+m>T)
ans[m][n]=a[m];
else
{
if(ans[m+a[m]][1]!=0)
temp=ans[m+a[m]][1];
else
temp=dfs(a[m]+m,1);
if(temp==-1)
ans[m][n]=-1;
else
ans[m][n]=a[m]+temp;
}
}
else
{
if(m-a[m]<=0)
ans[m][n]=a[m];
else
{
if(ans[m-a[m]][0]!=0)
temp=ans[m-a[m]][0];
else
temp=dfs(m-a[m],0);
if(temp==-1)
ans[m][n]=-1;
else
ans[m][n]=a[m]+temp;
}
}
return ans[m][n];
}
int main()
{
scanf("%d",&T);
for(int i=2; i<=T; i++)
scanf("%lld",&a[i]);
memset(ans,0,sizeof(ans));
ans[1][0]=-1;
for(int i=2; i<=T; i++)
for(int j=0; j<2; j++)
if(ans[i][j]==0)
{
memset(visit,false,sizeof(visit));
visit[1][0]=true;
dfs(i,j);
}
for(int i=2; i<=T; i++)
{
if(ans[i][1]==-1)
printf("-1\n");
else
printf("%lld\n",i-1+ans[i][1]);
}
}
跑到第九组数据果断TLE了,我也不知道为什么。后来读来读题目,不建议用%lld,应为我用linux编的,所以用了lld,改成I64d,还是TLE,a数组改成int的,还是TLE,这些都是小打小闹,更本不管用。
后来发现其实并不是所有的状态都要求出来,有些是用不到的,所以改成了把用到的求出来,也就是把dfs放到输出那里,只求对我有用的。可结果还是TLE了。
后来我觉的我的dfs写的太丑了,不要把这一步是加还是减分开搞,用一个叫mark的数组判断是加还是减,然后用位运算去改变状态,代码的改动是这样的:
mark[2]= {1,-1};
next=mark[n]*a[m]+m;//这样加还是减就不用判断了
dfs(next,1^n);//这里用亦或
当然这三行不是连着的,我只是把我觉的重要的地方找出来,并且我不是每次都初始化visit数组,而是初始化一次。然后代码就特别短,而且速度飞快,终于AC了。最终我也没弄清楚到底是哪一步把效率提高的,不想去折腾它了,什么时候有兴致了再看吧。
贴代码
#include "iostream"
#include "cstdio"
#include "cstring"
#include "cmath"
#include "stdlib.h"
#include "algorithm"
using namespace std;
long long ans[200010][2];
int a[200010],mark[2]= {1,-1},T;
bool visit[200010][2];
long long dfs(int m,int n)
{
if(visit[m][n]==true)
return ans[m][n];
visit[m][n]=true;
int next=mark[n]*a[m]+m;
if(next>T||next<=0)
ans[m][n]=a[m];
else
{
long long temp=dfs(next,1^n);
if(temp==-1)
ans[m][n]=-1;
else
ans[m][n]=(long long)a[m]+temp;
}
return ans[m][n];
}
int main()
{
scanf("%d",&T);
for(int i=2; i<=T; i++)
scanf("%d",&a[i]);
memset(ans,-1,sizeof(ans));
memset(visit,false,sizeof(visit));
visit[1][0]=true;
for(int i=2; i<=T; i++)
{
if(!visit[i][1])
dfs(i,1);
if(ans[i][1]==-1)
printf("-1\n");
else
printf("%I64d\n",i-1+ans[i][1]);
}
}
水平有限,众神轻喷。