[BZOJ1177][BZOJ1178][BZOJ1179]APIO2009解题报告
抱着好奇心态去开始做APIO的往年试题感受一下难度
Oil
Description
采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井。被拍卖的整块土地为一个矩形区域,被划分为M×N个小块。 Siruseri地质调查局有关于Navalur土地石油储量的估测数据。这些数据表示为M×N个非负整数,即对每一小块土地石油储量的估计值。 为了避免出现垄断,政府规定每一个承包商只能承包一个由K×K块相连的土地构成的正方形区域。 AoE石油联合公司由三个承包商组成,他们想选择三块互不相交的K×K的区域使得总的收益最大。 例如,假设石油储量的估计值如下: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 8 8 8 8 8 1 1 1 1 8 8 8 8 8 1 1 1 1 8 8 8 8 8 1 1 1 1 1 1 1 8 8 8 1 1 1 1 1 1 1 1 8 8 8 1 1 1 1 1 1 9 9 9 1 1 1 1 1 1 9 9 9 如果K = 2, AoE公司可以承包的区域的石油储量总和为100, 如果K = 3, AoE公司可以承包的区域的石油储量总和为208。 AoE公司雇佣你来写一个程序,帮助计算出他们可以承包的区域的石油储量之和的最大值。
首先由于是一个个正方形,所以很容易看出最终每块油田所在的区域是通过一些水平或竖直的线隔开的
并且这些线在它所在的区域内是到达边界的 这道题三个油田也就是最终只可能为下面六种情况
其中右边的两列看起来比较好处理
我们只要先预处理左上左下右上右下四个方向最大答案的前缀和
然后再枚举水平和竖直线的位置
稍加处理就可以得出答案
左边的一列以两条水平线为例
我们枚举一条水平线之后,枚举另一条在它下面的水平线
这条水平线每向下移一行,中间部分也就多了一行
所以我们可以预处理出每行每列的最大答案
在枚举第二条线的时候维护中间部分答案的最大值
上下部分用前缀和来计算
在写代码的过程中,由于我统计的是以(x,y)为左上角的边长为k的矩形的答案
所以前缀和部分用到的时候需要注意哪些地方需要-k+1,哪些地方是限制住的
时间复杂度O(nm)
/**************************************************************
Problem: 1177
User: mjy0724
Language: C++
Result: Accepted
Time:6128 ms
Memory:54256 kb
****************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int maxn = ;
int a[maxn][maxn],map[maxn][maxn],lu[maxn][maxn],ru[maxn][maxn],ld[maxn][maxn],rd[maxn][maxn],h[maxn],v[maxn];
int max(int a,int b){if (a>b) return a;else return b;}
int main(){
int n,m,k,ans=;
scanf("%d%d%d",&n,&m,&k);
for (int i=;i<=n;i++) for (int j=;j<=m;j++) scanf("%d",&map[i][j]);
for (int i=;i<=n;i++) for (int j=;j<=m;j++) map[i][j]+=map[i-][j];
for (int i=;i<=n;i++) for (int j=;j<=m;j++) map[i][j]+=map[i][j-];
for (int i=;i<=n-k+;i++) for (int j=;j<=m-k+;j++) a[i][j]=map[i+k-][j+k-]-map[i-][j+k-]-map[i+k-][j-]+map[i-][j-];
for (int i=;i<=n-k+;i++) for (int j=;j<=m-k+;j++) lu[i][j]=max(max(lu[i-][j],lu[i][j-]),a[i][j]);
for (int i=;i<=n-k+;i++) for (int j=m-k+;j>;j--) ru[i][j]=max(max(ru[i-][j],ru[i][j+]),a[i][j]);
for (int i=n-k+;i>;i--) for (int j=;j<=m-k+;j++) ld[i][j]=max(max(ld[i+][j],ld[i][j-]),a[i][j]);
for (int i=n-k+;i>;i--) for (int j=m-k+;j>;j--) rd[i][j]=max(max(rd[i+][j],rd[i][j+]),a[i][j]);
for (int i=;i<=n;i++) for (int j=;j<=m;j++) h[i]=max(h[i],a[i][j]),v[j]=max(v[j],a[i][j]);
for (int i=k;i<=n-*k;i++){
int mx = ;
for (int j=i+k;j<=n-k;j++){
mx = max(mx,h[j-k+]);
ans = max(ans,lu[i-k+][m-k+]+mx+ld[j+][m-k+]);
}
}
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m-k;j++) ans=max(ans,lu[i-k+][j-k+]+ru[i-k+][j+]+ld[i+][m-k+]);
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m-k;j++) ans=max(ans,lu[i-k+][m-k+]+ld[i+][j-k+]+rd[i+][j+]);
for (int i=k;i<=m-*k;i++){
int mx=;
for (int j=i+k;j<=m-k;j++){
mx = max(mx,v[j-k+]);
ans = max(ans,lu[n-k+][i-k+]+mx+ru[n-k+][j+]);
}
}
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m-k;j++) ans=max(ans,lu[i-k+][j-k+]+ru[n-k+][j+]+ld[i+][j-k+]);
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m-k;j++) ans=max(ans,lu[n-k+][j-k+]+ru[i-k+][j+]+rd[i+][j+]);
printf("%d\n",ans);
return ;
}
[BZOJ1178]CONVENTION会议中心
Description
Siruseri政府建造了一座新的会议中心。许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议。 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂。会议中心的销售主管认为:最好的策略应该是将会堂租借给尽可能多的客户。显然,有可能存在不止一种满足要求的策略。 例如下面的例子。总共有4个公司。他们对租借会堂发出了请求,并提出了他们所需占用会堂的起止日期(如下表所示)。 开始日期 结束日期 公司1 4 9 公司2 9 11 公司3 13 19 公司4 10 17 上例中,最多将会堂租借给两家公司。租借策略分别是租给公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意会议中心一天最多租借给一个公司,所以公司1和公司2不能同时租借会议中心,因为他们在第九天重合了。 销售主管为了公平起见,决定按照如下的程序来确定选择何种租借策略:首先,将租借给客户数量最多的策略作为候选,将所有的公司按照他们发出请求的顺序编号。对于候选策略,将策略中的每家公司的编号按升序排列。最后,选出其中字典序最小1的候选策略作为最终的策略。 例中,会堂最终将被租借给公司1和公司3:3个候选策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。 你的任务是帮助销售主管确定应该将会堂租借给哪些公司。
这道题我觉得非常有意思,用一些简单的算法拼凑出了一个很好的解题思路,虽然代码复杂度略高
首先第一问的答案很显然...直接贪心就可以出解
我们可以考虑两段时间区间,如果其中一段包含另一段,那么显然这条长的线段是没有用的
因为在每条线段对答案的贡献都=1的情况下,选这条短的能为其他线段的摆放留有更多空间
我们把所有的这种不优秀的线段都删除掉以后,将它们按左端点从小到大排序之后
会发现右端点也是升序,因为否则就会出现包含的情况
可以考虑取了某条线段i以后,实际上下一条取哪条线段已经确定了
因为在可以取的范围(左端点大于线段i的右端点)的线段集合中,我们肯定取那个右端点最小的
由于我们上面得出左端点与右端点同时上升,二分找出第一个左端点大于线段i右端点的线段就是下一条要取的线段
这样一来好像忽然明白了:其实只要确定了起点,整个要取的线段序列都是确定的
我们可以用倍增来储存第i条线段接下去要取的第2^j条线段
直接按照题目的要求来,从小到大去试
只要当前线段能够放下(即在此之前的线段没有覆盖到这条线段所在的位置)
咦那为什么不能让之前的移一下呢?我们的顺序是从小到大,人家的优先级比你要高
所以这样做有一个比较好的性质:确定了的就不能修改
另外,还要保证放下了之后最优解不变,我们设当前线段所在的空白位置向左右延伸到的最远处分别是lx,rx,当前线段(l,r)
那么ans(lx,rx)=ans(l,lx-1)+ans(r+1,rx)+1才能够保证最优解不变
那么获取答案的函数ans如何求得呢?
上上段我们处理好了倍增的数组,ans即求一个给定起点和终点的线段区间的答案
直接用倍增处理就可以了
/**************************************************************
Problem: 1178
User: mjy0724
Language: C++
Result: Accepted
Time:5804 ms
Memory:85588 kb
****************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
const int maxn=,INF=;
struct point{
int x,y;
}a[maxn],c[maxn],p[maxn];
struct tree{
int l,r,lx,rx,cov;
}tr[maxn*];
int n,tot,fa[maxn][],ans,as[maxn],d[maxn],e[maxn];
bool vis[maxn];
char s[];
int max(int a,int b){
if (a>b) return a;
else return b;
}
int min(int a,int b){
if (a<b) return a;
else return b;
}
void sortf(int l,int r){
int i=l,j=r,mid=(l+r) >> ,midx=a[mid].x,midy=a[mid].y;
do{
while (i<r&&((a[i].x>midx)||(a[i].x==midx&&a[i].y<midy))) i++;
while (l<j&&((a[j].x<midx)||(a[j].x==midx&&a[j].y>midy))) j--;
if (i<=j){
a[]=a[i];a[i]=a[j];a[j]=a[];
i++;j--;
}
}while (i<=j);
if (i<r) sortf(i,r);
if (l<j) sortf(l,j);
}
void sortg(int l,int r){
int i=l,j=r,mid=p[(l+r) >> ].x;
do{
while (i<r&&p[i].x<mid) i++;
while (l<j&&p[j].x>mid) j--;
if (i<=j){
p[]=p[i];p[i]=p[j];p[j]=p[];
i++;j--;
}
}while (i<=j);
if (i<r) sortg(i,r);
if (l<j) sortg(l,j);
}
int find(int x){
int ans=,l=,r=tot;
while (l<=r){
int mid=(l+r) >> ;
if (p[mid].x>=x){
ans=mid;r=mid-;
}else l=mid+;
}
return ans;
}
void solve(){
int minr=INF;
memset(vis,true,sizeof(vis));
int j;
for (int i=;i<=n;i=j){
for (j=i;a[i].x==a[j].x&&j<=n;j++){
if (minr<a[j].y) vis[j]=false;
if (a[j].y<minr) minr=a[j].y;
}
}
tot=;
for (int i=;i<=n;i++) if (vis[i]) p[++tot]=a[i];
sortg(,tot);
for (int i=;i<=tot;i++) fa[i][]=find(p[i].y+);
for (int j=;( << j)<=n;j++)
for (int i=;i<=tot;i++) fa[i][j]=fa[fa[i][j-]][j-];
}
void build(int p,int l,int r){
tr[p].l=l;tr[p].r=r;tr[p].lx=INF;tr[p].rx=-INF;tr[p].cov=;
if (l!=r){
int mid = (l+r) >> ;
build(p<<,l,mid);build((p<<)+,mid+,r);
}
}
void sorth(int l,int r){
int i=l,j=r,mid=d[(l+r) >> ];
do{
while (i<r&&d[i]<mid) i++;
while (l<j&&d[j]>mid) j--;
if (i<=j){
int tem=d[i];d[i]=d[j];d[j]=tem;
i++;j--;
}
}while (i<=j);
if (i<r) sorth(i,r);
if (l<j) sorth(l,j);
}
void ls(){
for (int i=;i<=n;i++){
d[++d[]]=a[i].x;d[++d[]]=a[i].y;
}
sorth(,d[]);
int j;
for (int i=;i<=d[];i=j){
e[++e[]]=d[i];j=i+;
while (j<=d[]&&d[i]==d[j]) j++;
}
}
void insert(int p,int l,int r){
if (tr[p].cov) {tr[p].lx=tr[p].l,tr[p].rx=tr[p].r;}
if (tr[p].l==l&&tr[p].r==r){
tr[p].cov=;tr[p].lx=tr[p].l;tr[p].rx=tr[p].r;
return;
}
int mid = (tr[p].l+tr[p].r) >> ;
if (tr[p].cov) tr[p<<].cov=,tr[(p<<)+].cov=;
if (r<=mid) insert(p<<,l,r);else
if (l>mid) insert((p<<)+,l,r);else{
insert(p<<,l,mid);insert((p<<)+,mid+,r);
}
tr[p].lx=min(tr[p<<].lx,tr[(p<<)+].lx);
tr[p].rx=max(tr[p<<].rx,tr[(p<<)+].rx);
}
int getnum(int x){
int l=,r=e[];
while (l<=r){
int mid=(l+r) >> ;
if (e[mid]==x) return mid;
if (x<e[mid]) r=mid-;else l=mid+;
}
}
int getl(int p,int l,int r){
if (tr[p].cov){tr[p].lx=l;tr[p].rx=r;}
if (tr[p].l==l&&tr[p].r==r) return tr[p].lx;
int mid=(tr[p].l+tr[p].r) >> ;
if (tr[p].cov) tr[p<<].cov=,tr[(p<<)+].cov=;
if (r<=mid) return getl(p<<,l,r);else
if (l>mid) return getl((p<<)+,l,r); else
return min(getl(p<<,l,mid),getl((p<<)+,mid+,r));
}
int getr(int p,int l,int r){
if (tr[p].cov){tr[p].lx=l;tr[p].rx=r;}
if (tr[p].l==l&&tr[p].r==r) return tr[p].rx;
int mid=(tr[p].l+tr[p].r)>>;
if (tr[p].cov) tr[p<<].cov=,tr[(p<<)+].cov=;
if (r<=mid) return getr(p<<,l,r);else
if (l>mid) return getr((p<<)+,l,r);else
return max(getr(p<<,l,mid),getr((p<<)+,mid+,r));
}
int query(int x,int y){
if (x<||y<||x>e[]||y>e[]) return ;
int s=-,l=,r=tot;
while (l<=r){
int mid=(l+r) >> ;
if (p[mid].x>=e[x]) s=mid,r=mid-;else l=mid+;
}
int t=-;
l=;r=tot;
while (l<=r){
int mid=(l+r) >> ;
if (p[mid].y<=e[y]) t=mid,l=mid+;else r=mid-;
}
if (s==-||t==-||s>t) return ;
int ans=,i=log(tot)/log();
while (s<t&&i>=){
while (s<t&&fa[s][i]!=&&fa[s][i]<=t) s=fa[s][i],ans+=<<i;
i--;
}
return ans;
}
int main(){
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y),gets(s);
for (int i=;i<=n;i++) c[i].x=a[i].x,c[i].y=a[i].y;
ls();
sortf(,n);
solve();
build(,,e[]+);
insert(,,);insert(,e[]+,e[]+);
for (int i=;i<=n;i++) c[i].x=getnum(c[i].x),c[i].y=getnum(c[i].y);
for (int i=;i<=n;i++) if (getl(,c[i].x,c[i].y)==INF&&getr(,c[i].x,c[i].y)==-INF){
int ll=getr(,,c[i].x-),rr=getl(,c[i].y+,e[]+);
if (query(ll+,rr-)==query(ll+,c[i].x-)+query(c[i].y+,rr-)+) {
as[++ans]=i;insert(,c[i].x,c[i].y);
}
}
printf("%d\n",ans);
for (int i=;i<=ans;i++) printf("%d ",as[i]);
return ;
}
[BZOJ1179]Atm
Description
这大概是这套题目中最简单的一道了...
首先我们可以看出,题目中说的,可以经过一条边多次其实就是要处理环的情况
取款机里的钱都是非负整数,能拿的没有理由不要
因此如果进入了一个环,就可以把环中所有的钱都取光,然后从任意一个点出去
思路已经很明确了,tarjan缩点,同时累计每一个环的现金总数和是否存在一个酒吧
缩完点的图我们重新连上边(自环不连),就变成了一个有向无环图
我们可以用拓扑序DP来解决这个问题
对于给定的起点,这样类似的题目之前也做过
我的做法是先从起点开始dfs一次,重新统计入度
然后再进行DP,最后在那些有酒吧坐落的环或点答案中挑出一个最大的输出
/**************************************************************
Problem: 1179
User: mjy0724
Language: C++
Result: Accepted
Time:6804 ms
Memory:52260 kb
****************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int maxn = ;
int cnt,time,top,n,m,s,p,e,fa[maxn],next[maxn],link[maxn],x[maxn],y[maxn],b[maxn];
int stack[maxn],low[maxn],dfn[maxn],pos[maxn],a[maxn],w[maxn],opt[maxn],f[maxn];
bool vis[maxn],ins[maxn],isp[maxn],isbar[maxn];
void add(int x,int y){
fa[++e]=y;next[e]=link[x];link[x]=e;
}
int min(int a,int b){
if (a<b) return a;
return b;
}
int max(int a,int b){
if (a>b) return a;
return b;
}
void tarjan(int p){
dfn[p]=low[p]=++time;stack[++top]=p;
vis[p]=false;ins[p]=true;
for (int i=link[p];i!=;i=next[i])
if (vis[fa[i]]){
tarjan(fa[i]);
low[p]=min(low[p],low[fa[i]]);
}else if (ins[fa[i]]) low[p]=min(low[p],dfn[fa[i]]);
if (low[p]==dfn[p]){
cnt++;
do{
pos[stack[top]]=cnt;
a[cnt]+=w[stack[top]];
isbar[cnt]=isbar[cnt] or isp[stack[top]];
ins[stack[top--]]=false;
}while (stack[top+]!=p);
}
}
void dfs(int p){
vis[p]=false;
for (int i=link[p];i;i=next[i]){
b[fa[i]]++;
if (vis[fa[i]]) dfs(fa[i]);
}
}
void solve(int s){
int head=,tail=;opt[]=s;f[s]=a[s];
while (head!=tail){
int x=f[opt[++head]];
for (int i=link[opt[head]];i;i=next[i]){
f[fa[i]]=max(f[fa[i]],x+a[fa[i]]);
b[fa[i]]--;
if (!b[fa[i]]) opt[++tail]=fa[i];
}
}
int ans=;
for (int i=;i<=cnt;i++) if (isbar[i]) ans=max(ans,f[i]);
printf("%d\n",ans);
}
int main(){
scanf("%d%d",&n,&m);
e=;
for (int i=;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}
for (int i=;i<=n;i++) scanf("%d",&w[i]);
scanf("%d%d",&s,&p);
memset(isp,false,sizeof(isp));
for (int i=;i<=p;i++){
int tmp;
scanf("%d",&tmp);
isp[tmp]=true;
}
time=;top=;
memset(vis,true,sizeof(vis));
memset(ins,false,sizeof(ins));
memset(isbar,false,sizeof(isbar));
for (int i=;i<=n;i++) if (vis[i]) tarjan(i);
memset(link,,sizeof(link));
memset(next,,sizeof(next));
e=;
for (int i=;i<=m;i++) if (pos[x[i]]!=pos[y[i]]) add(pos[x[i]],pos[y[i]]);
memset(vis,true,sizeof(vis));
dfs(pos[s]);
solve(pos[s]);
return ;
}
[BZOJ1177][BZOJ1178][BZOJ1179]APIO2009解题报告的更多相关文章
- CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...
- 二模13day1解题报告
二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...
- BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353[S ...
- 习题:codevs 2822 爱在心中 解题报告
这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...
- 习题:codevs 1035 火车停留解题报告
本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...
- 习题: codevs 2492 上帝造题的七分钟2 解题报告
这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...
- 习题:codevs 1519 过路费 解题报告
今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...
- NOIP2016提高组解题报告
NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合
- LeetCode 解题报告索引
最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中...... ...
随机推荐
- Android 网络编程 API笔记 - java.net 包相关 接口 api
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- 算法与数据结构实验题 6.3 search
★实验任务 可怜的 Bibi 刚刚回到家,就发现自己的手机丢了,现在他决定回头去搜索 自己的手机. 现在我们假设 Bibi 的家位于一棵二叉树的根部.在 Bibi 的心中,每个节点 都有一个权值 x, ...
- 软工实践Alpha冲刺(3/10)
队名:我头发呢队 组长博客 作业博客 杰(组长) 过去两天完成了哪些任务 继续翻阅Google Material Design 2的官方文档 接下来的计划 音源爬取 还剩下哪些任务 app开发 燃尽图 ...
- <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>
<Item 9> Never call virtual functions during construction or destruction 1.you shouldn't call ...
- Qt自定义标题栏
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt自定义标题栏 本文地址:http://techieliang.com/2017/1 ...
- 【week2】Scrum中的站立会议
Scrum站立会议 站立会议给我的第一印象就是站着开会,在经过我查阅资料之后,发现也是差不多的意思.学术一点的分析就是在Sprint开始后,团队将会在每个工作日特定时间举行一个简短会议,每次会议 ...
- [剑指Offer] 62.二叉搜索树的第k个结点
题目描述 给定一颗二叉搜索树,请找出其中的第k大的结点.例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4. [思路]遍历二叉搜索树,存入一个vector ...
- UDP发送的数据 以数据包形式发送
UDP发送的数据 以数据包形式发送
- 【bzoj1038】瞭望塔 半平面交
题目描述 致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安.我们将H村抽象为一维的轮廓.如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), ( ...
- Android UI设计的基本元素有哪些
在android app开发如火如荼的今天,如何让自己的App受人欢迎.如何增加app的下载量和使用量....成为很多android应用开发前,必须讨论的问题.而ui设计则是提升客户视觉体验度.提升下 ...