A. Golden Plate
水题,公式都不用推,循环就完事了。
http://codeforces.com/contest/1072/problem/A
int n,m,k;
cin>>n>>m>>k;
int ans=0;
while(k--){
ans+=2*n+2*m-4;
n-=4;
m-=4;
}
cout<
B. Curiosity Has No Limits
题意是给你两个生成函数和两个生成出来的数列,让你去找原数列(也可能不存在)。我的做法是,对于任意的i,ai和bi总共只有16种不同的可能,穷举他们,去找ti和ti+1可能的存在形式。发现除了一组ai和bi可以得到两组ti和ti+1外,其他的至多只能推出一种。那么我们穷举可能t1和t2,可以在O(n)时间内根据上式推出后面的元素。如果每一种都推不出,那么说明无解。
http://codeforces.com/contest/1072/problem/B
const int maxn=2e5+7;
struct node{
int x,y;
}c[maxn];
int a[maxn],b[maxn];
int n;
int err=0;
int ans[maxn];
void check(){
if(err==-1)return;
for(int i=2;i
PS:不过这题我写的麻烦了,因为显然对于每一个ai(或者bi)我们可以把它拆成两位,每一位对应t中间相对的位,这样拆位之后处理就可以更加简便,因为变量更少。当然对复杂度不大。但是这样可以处理更大规模的问题,如果ai的范围是0-255,那么如果不拆位的话难度就过于大了。所以拆位在处理位运算的时候是一种非常重要的思想。
C. Cram Time
贪心题。显然我们不难发现,答案ans的上界满足如下性质:
①(ans+1)*ans/2<=(a+b)
那么接下来我们希望证明该上界就是我们需要的答案。
我一开始想的是dp,但是稍加思索之后发现,这个上界是一定能够取得的,以下给出证明:
1.若MIN(a,b)<=ans,那么我们对把第MIN(a,b)天安排在a和b中较小的那一天,由公式①可知剩下的时间一定可以安排在b里面。
2.若MIN(a,b)>ans,那么把第ans天安排在较小的那一天,就构成了一个ans-1的子问题,重复本步骤即可,最后必定会在步骤1中得到解。
LL n,m,sum,maxn;
LL ans1[100005],ans2[100005],cnt1=0,cnt2=0,sum1=0,sum2=0;
int main()
{
cin>>n>>m;
sum=m+n;
LL l=0,r=n+m+1;
while(l>1;
if((mid+1)*mid<=(n+m)*2)l=mid;
else r=mid;
}
maxn=l;
for(int i=maxn;i;--i){
if(sum1+i<=n){
sum1+=i;
ans1[++cnt1]=i;
}
else{
sum2+=i;
ans2[++cnt2]=i;
}
}
cout<
D. Minimum path
不难看出,这是很裸的一道dp题。
要这么说其实也对,至少第一个代码段是dp。问题的第一部分还是比较简单,用dp保存还能将多少个非a变成a,然后考虑当前格子是否是a,然后将变掉的字母换成a就可以了。
接下来我们就要找字典序最小的串了,那么很容易想到我们熟悉的dp:
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+c[i][j]
其中dp保存到第i行第j列的字典序最小字符串,c保存图中第i行第j列的字母。
如果这样粗暴的存,那么是一个O(N^3)的空间复杂度,给定的n=2000,大概率会爆空间,所以我把它压缩成了一个一维的dp:
dp[j]=max(dp[j-1],dp[j])+c[i][j]
考虑第i行第j列的状态,那么他或者从左边来,或者从上边来。在没有访问过本格的情况下,dp[j]里面存的其实就是上一上本列的状态,而dp[j-1]则因为已经被更新过,所以存储的是本行j-1列的状态。
然后算算时间复杂度O(N^3),考虑到我的常数非常小,我认为应该可以通过,果不其然,一发WA。
然后我学习到了一种更加巧妙的dp方法,虽然dp本身仍然是O(N^2),但是免去了比较string的时间消耗。我们用一个(bool)used来表示某个点是否可能是最优解中的点,显然,出发点必定在其中。接下来,我们第一重循环穷举k=i+j(0
初始化:当k==0时,只有一个元素,显然成立
保持:当在第k-1层满足该性质时,对于每有被选中的数字,无非包括两种情况。一种是未被上层used连接的(无论大小),那么由于字典序的性质,如果他被选上,那么之前一定不是最优的,所以舍去。另一种是被上一层连接但不是最小的,那么当然也不是最优的,所以舍去。剩下的满足条件,且因为上一层至少有一个在used中的,所以本层也至少能找到一个放进used中。
终止:在到达最后一层时停止。
显然每一层放入used的字母是唯一值,我们保存他们加入used的顺序,就可以得到字典序最小的答案。
char c[2005][2005];
int lt[2005][2005];
bool used[2005][2005];
int n;
int now=1;
int maxx=0;
int getlastlt(int i,int j){
if(!i)return lt[i][j-1];
if(!j)return lt[i-1][j];
return MAX(lt[i][j-1],lt[i-1][j]);
}
string MINn(string x,string y){
int la=x.size(),lb=y.size();
int p=0;
while(py[p])return y;
p++;
}
return x;
}
int main()
{
INI(lt);
INI(used);
cin>>n>>lt[0][0];
for(int i=0;i=0){
if((rr&&used[rr-1][cc])||(cc&&used[rr][cc-1]))minn=MIN(minn,c[rr][cc]),used[rr][cc]=1;
++cc;
--rr;
}
rr=i;
cc=0;
while(rr>=0){
if(c[rr][cc]!=minn)used[rr][cc]=0;
++cc;
--rr;
}
}
else{
rr=n-1;
cc=i-rr;
while(cc=0){
if(c[rr][cc]!=minn)used[rr][cc]=0;
++cc;
--rr;
}
}
ans.push_back(minn);
}
cout<