A. HDU 2046
第一次看到这个,题目,想到的是动态规划,真是思维僵化了,总想套,而如果仔细想它的构成方法,就会很容易想到汉诺塔的那个想法,面对复杂的问题,先思考距离胜利一步之遥的时候要怎么解决问题。n层的状态数=n-1层状态数+n-2层状态数+1-1。如果有2层,有2种方法把它填满,竖的和横的,但是其中竖的会和补一层的相互重复,因为补1层只能用竖的。
最开始的时候发现自己思维僵化后,很自然的想到了这个,却wa了,唉,觉得是自己想错了,就又换了好多种,wa绝望,后来发现是这是一个feibonaqie数列,数会很大,应该用long long int,可惜想到这个的时候也是在换其他方法来算的,后来看了答案,才知道,自己是错在了这里。
#include<cstdio>
#include<cstring>
using namespace std;
int main(void)
{long long int all[60];
memset(all,0,sizeof(all));
all[1]=1;
all[0]=1;
for(int i=2;i<=50;i++)
all[i]+=all[i-1]+all[i-2];
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%lld\n",all[n]);
}
return 0;
}
B.HDU 5247
这题我觉得就是考验心态,敢不敢写,很容易想到的思路是,每次就选几个数,用sort排序,然后再检查是否符合,这自然会tle,但这题思路就是这样子,如果敢想,肯定可以找出简化的方法,比如说排序上,每次重新sort这肯定多浪费时间,因此可以想到打表,在排序上,如果用sort,那么它要重排,而实际上,只要把一个数放到一个位置上就行,可以自己写一个sort。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e4+10;
long long int all[maxn];
int ans[1009],fin[1009];
long long int wait[1009];
bool check(int a)
{
for(int i=1;i<a;i++)if((wait[i+1]-wait[i])!=1){return 0;}
return 1;
}
void zisort(int to)
{
for(int i=to;i>1;i--)
if(wait[i]<wait[i-1])swap(wait[i],wait[i-1]);
else return;
}
int main(void)
{int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&all[i]);
//确定表的大小
int big=0;
for(int i=1;i<=m;i++){scanf("%lld",&fin[i]);big=max(big,fin[i]);}
//打表
for(int i=1;i<n;i++)
{wait[1]=all[i];
for(int d=2;d<=big;d++)
{if((i+d-1)>n)break;
wait[d]=all[i+d-1];
zisort(d);
if(check(d))ans[d]++;
}
}
ans[1]=n;
//输出
printf("Case #1:\n");
for(int i=1;i<=m;i++)
printf("%d\n",ans[fin[i]]);
return 0;
}
D. POJ 2299——离散化和树状数组
这题拿到的时候真的想到的都是tle的做法,首先在排序上要快排一次,其次每次找要走一遍,双重循环。后来看题解,真是自己缺的知识。
先想排数组,相邻交换,就是对于第n个,发现前面有a个小于它的,而现在它新添在的位置是第n个,所以他要交换 n-a-1次,如此就得到前n个排序好的数组了,每次都如此。
对于要查找a数组前n个的和 且要实时更新 用树状数组。
https://blog.csdn.net/Small_Orange_glory/article/details/81290634 树状数组的学习链接
总结起来就是求和就 while(a>0){cnt+=p[a];a-=lowbit(a);},
更新就是while(a<=n){p[a]+=1;a+=lowbit(a);} lowbit(a) {return a&(-a);}
而用树状数组,既然是开数组,那就不能太大,但是这个题的数据范围大,所以要想到用离散化的方法,即把数组的大小排序号变为值,但是在这个地方又有一个技巧,用结构体,根据值排好虚后,结构体的成员又保留了原来的序列号,这时再把值变为排好后的序列号。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5e5+10;
int n;
struct re
{
int val,id;
bool operator<(const re &a)const {return val!=a.val? val<a.val:id<a.id;}
}all[maxn];
int now[maxn];
int num[maxn];
inline int lowbit(int a)
{
return a&(-1*a);
}
int miele(int a)
{
int cnt=0;
while(a>0)
{cnt+=num[a];
a-=lowbit(a);
}
return cnt;
}
void add(int a)
{
while(a<=n)
{
num[a]++;
a+=lowbit(a);
}
}
int main(void)
{
while(scanf("%d",&n)&&n)
{
for(int i=1;i<=n;i++){scanf("%d",&all[i].val);all[i].id=i;}
sort(all+1,all+1+n);
for(int i=1;i<=n;i++)now[all[i].id]=i;
long long int ans=0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
int f=i-miele(now[i])-1;
ans+=f;
//printf("f=%d\n",f);
add(now[i]);
}
printf("%lld\n",ans);
}
return 0;
}
E.HDU 1175 -------dfs
题目没看清楚明白,它是有要求的,不能转超过2次,每次dfs的时候要注意,由于这条路径走不成功可能是因为zuan太多次,但是可能有另一条路径可以走通,但是要走过这个点,因此但失败return后要记vi=0;此外zuan的时候也要注意
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1009;
int step[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}};
int n,m;
bool suc;
int mp[maxn][maxn];
bool vi[maxn][maxn];
void dfs(int ax,int ay,int bx,int by,int last,int zuan)
{if(zuan>2)return;
for(int i=1;i<=4;i++)
{
int nx=ax+step[i][0];
int ny=ay+step[i][1];
int p=0;
if((i-1)/2!=(last-1)/2)p=1;
if(last==6)p=0;
if(nx==bx&&ny==by&&(zuan+p)<=2){suc=1;return;}
if(mp[nx][ny]!=0||vi[nx][ny])continue;
if(nx<1||ny<1||nx>n||ny>m)continue;
vi[nx][ny]=1;
dfs(nx,ny,bx,by,i,zuan+p);
vi[nx][ny]=0;
if(suc)return;
}
}
int main(void)
{
while(scanf("%d %d",&n,&m)&&n&&m)
{
for(int i=1;i<=n;i++)
for(int d=1;d<=m;d++)
scanf("%d",&mp[i][d]);
int num;
scanf("%d",&num);
for(int i=1;i<=num;i++)
{int ax,ay,bx,by;
scanf("%d %d %d %d",&ax,&ay,&bx,&by);
if(mp[ax][ay]!=mp[bx][by]||mp[ax][ay]==0||mp[bx][by]==0){printf("NO\n");continue;}
suc=0;
memset(vi,0,sizeof(vi));
dfs(ax,ay,bx,by,6,0);
if(suc)printf("YES\n");else printf("NO\n");
}
}
return 0;
}
F. HDU 2571
第一眼看完,直觉是动态规划裸题,直接打码,样例过了,交,wa。对于这种第一直觉套模板总让我很害怕,因为模板可能是因为太常见的原因,每次用快速地没有自习思考。仔细去想这题,动态规划的原理,是每一步都是选择最优的上一步,而每一步都是从左走过来,从上走过来,所以用动态规划从一行行向右过去,是没有错的,
但是有一个边境问题,比如说每次都是max(now[i-1][d],now[i][d-1]);但如果这个now[i][d-1]不在这个地图上,而在初始的时候把所有点都设有0,而now[i-1][d]是负数,那么就错了。
此外,now[i][d]还要有一个初始值,但是由于每个格子可能会有特殊性,不能固定选择左边的那个,或者是上边的那个就是它的初始值,而这样的特殊格子有第1格,第一行,第一列,就要特别的给初始值,其他的可以是左边或者上边。
ac代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int mp[30][2000];
int now[30][3000];
int main(void)
{int cas;
scanf("%d",&cas);
while(cas--)
{int r,c;
memset(now,0,sizeof(now));
memset(mp,0,sizeof(mp));
scanf("%d %d",&r,&c);
for(int i=1;i<=r;i++)
for(int d=1;d<=c;d++)
{scanf("%d",&mp[i][d]);
}
now[1][1]=mp[1][1];
for(int i=1;i<=r;i++)
for(int d=1;d<=c;d++)
{
if(i==1&&d==1)continue;
if(i==1)now[i][d]=now[i][d-1];
else if(d==1)now[i][d]=now[i-1][d];
else now[i][d]=now[i-1][d];
for(int f=2;d/f>0;f++)
{
if(d%f)continue;
now[i][d]=max(now[i][d],now[i][d/f]);
}
if(i>2)now[i][d]=max(now[i][d],now[i-1][d]);
if(d>2)now[i][d]=max(now[i][d],now[i][d-1]);
now[i][d]+=mp[i][d];
}
printf("%d\n",now[r][c]);
}
return 0;
}