之前写了一篇博客。《【Android实战】记录自学自己定义GifView过程,具体解释属性那些事!

【学习篇】》

关于自己定义GifView的,具体解说了学习过程及遇到的一些类的解释,然后完毕了一个项目,能通过在xml增加自己定义 view (MyGifView)中增加自己定义属性(my:gif_src = “@drawable/coffee”)。达到播放gif图片的效果。


可是。有几个问题

1.gif_src 属性仅仅支持 gif 图,并不支持其它类型的图片

2.仅仅支持默认的引用图片,不能另外设置


问题一

gif_src 属性仅仅支持 gif 图,并不支持其它类型的图片。

解决思路:

ImageView本身有个属性 src 是定义好的。已经能够用它播放静态图片。假设再能通过它播放动态图片,不就解决这个问题啦?!

于是查看 ImageView 类的源代码,看到构造函数

public ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
//... final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
if (d != null) {
setImageDrawable(d);
}
//...
}

有没有非常眼熟?!

对,之前自己定义属性的时候用过!这里只是把属性路径改了。之前我们用的是自己定义的路径 R.styleable.GifView 。

于是乎。我也想着,要是能在继承类(MyGifView)里面复制上段代码,然后再用movie转化,转换成功说明是 gif。就用之前的方法播放。转换失败说明是其它格式的图片,就交给 ImageView 自己处理!

真是好办法!

然而。根本不能这么用:

不能导入internal的包!

可是思路是对的!

通过參考《 Android PowerImageView实现。能够播放动画的强大ImageView》

得知了能够用反射!

中心代码:

  /**
* 通过Java反射,获取到src指定图片资源所相应的id。 *
* @param a 属性组
* @param context
* @return 返回布局文件里指定图片资源所相应的id,没有指定不论什么图片资源就返回0。
*/
private int getResourceId(TypedArray a, Context context) {
try {
Field field = TypedArray.class.getDeclaredField("mValue");
field.setAccessible(true);
TypedValue typedValueObject = (TypedValue) field.get(a);
return typedValueObject.resourceId;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (a != null) {
a.recycle();
}
}
return 0;
}

之前在自己定义view初始化中的代码。我是用得到自己定义属性值的方法获取gif的数据

int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src属性相应值

如今仅仅须要改这一句就好啦!

//int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src属性相应值

int resId = getResourceId(typedArray, context); //src属性相应值

然后后面都不用改啦!

(可是转换成 InputStream 的时候。还是要加一句推断 if (resId != 0)再进行转换)

if (resId != 0) {
InputStream iStream = getResources().openRawResource(resId); //此方法能通过资源文件id查找到资源文件并转化为输入流
mMovie = Movie.decodeStream(iStream); //输入流转化为Movie (mMovie 为全局变量,类型 Movie)
}

好了问题解决。如今能够在xml用src属性指向.gif文件,并且进行正常播放了!

问题二

仅仅支持默认的引用图片,不能另外设置

解决思路:

从外面设置无非就是外面调用setImageResource(int resId)setImageDrawable(Drawable drawable)setImageBitmap(Bitmap bm)等这些方法去改变 ImageView 属性 src 所相应的值!

那么。重写这些方法。把资源改成我们的 movie 就好啦。so easy!

首先重写setImageResource(int resId)

@Override
public void setImageResource(int resId) {
if (resId != 0) {
InputStream iStream = getResources().openRawResource(resId);
setMovie(iStream);
if (mMovie == null) {
super.setImageResource(resId);
}
} else {
super.setImageResource(resId);
}
invalidate();
}
/**
* 设置movie
* @param iStream 输入流
*/
public void setMovie(InputStream iStream) {
mMovie = Movie.decodeStream(iStream);
if (mMovie == null) { //说明不是gif。直接退出
return;
}
//设置图片宽高
Bitmap bitmap = BitmapFactory.decodeStream(iStream);
if (bitmap == null) {
return;
}
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
bitmap.recycle();
}

然后在外面(比方MainActivity),调用gifView.setImageResource(R.drawable.coffee),能够显示gif,其它格式的图片也能够正常显示。

but…

出现了一个bug…

就是如今必须在xml里面的自己定义MyGifView增加默认的 src 引用 或者 backgroud 附初始值,不然会报错崩溃,假设不想增加默认图片。能够把background设置为透明 #00000000

报错的原因,大概是没设置src属性时。调用反射int resId = getResourceId(typedArray, context);得到的 resId 也并不为0 (具体得到的是什么我也还不知)。然后进入 if 语句执行InputStream iStream = getResources().openRawResource(resId);转换流的时候报了空指针,导致程序崩溃。

依照设置默认src或者backgroud的方法能够临时解决。假设广大网友知道是什么原因,有什么更好的办法解决它,恳求告知一下!

接着重写setImageDrawable(Drawable drawable)setImageBitmap(Bitmap bm)

=-=-=-=-=-=-=-=-= 为了完毕以下的实现,另花费了非常久时间,思绪可能和前面不大连贯了 =-=-=-=-=-=-=-=-=

我本来以为会如setImageResource(int resId)一样顺利,但事实上按原来的思路并不能够转换成gif,而是转换成了png/jepg!最后还是借助了三方包才勉强完毕任务。

接下来我按顺序解说下我实现的过程。

首先讲下原来的想法以及为什么后来推翻了。

按着setImageResource(int resId)的实现思路。setImageDrawable(Drawable drawable)应该也就是把 drawable 先转换成 input sream,然后再转换成movie,假设成功就说明是gif,不成功说明是其它格式则调用父类方法。

@Override
public void setImageDrawable(Drawable drawable) {
if(drawable == null) {
super.setImageDraable(drawable);
} else {
mWidth = drawable.getIntrinsicWidth(); //获得宽
mHeight = drawable.getIntrinsicHeight(); //获得高
InputStream iStream = FomatUtils.Drawable2InputStream(drawable); //通过工具类将drawable转换成input stream setMovie(iStream);
if (mMovie == null) { //说明不是gif
super.setImageDrawable(drawable);
}
}
invalidate();
} @Override
public void setImageBitmap(Bitmap bm) {
this.setImageDrawable(new BitmapDrawable(getContext().getResources(), bm));
}

这个时候我们在Activity增加语句调用setImageDrawable()方法。在执行,出现的是静态图片!

说明代码是有问题的,焦点在drawable转换成inputstream的地方

InputStream iStream = FomatUtils.Drawable2InputStream(drawable); //通过工具类将drawable转换成input stream

看下转换类的具体代码 (參考《Android Bitmap与DrawAble与byte[]与InputStream之间的转换工具类》)

/**
* Bitmap与DrawAble与byte[]与InputStream之间的转换工具类
* @author azz
*/
public class FormatUtils {
/**
* drawable -> input stream
*/
public InputStream Drawable2InputStream(Drawable d) {
//drawable -> bitmap
Bitmap bitmap = this.Drawable2Bitmap(d);
//bitmap -> input stream
return this.Bitmap2InputStream(bitmap);
}
/**
* drawable -> bitmap
*/
public Bitmap Drawable2Bitmap(Drawable drawable) {
Bitmap bitmap = Bitmap
.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
/**
* bitmap -> input stream
*/
public InputStream Bitmap2InputStream(Bitmap bm, int quality) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, quality, baos); //就是这里限制了转换成PNG类型
InputStream is = new ByteArrayInputStream(baos.toByteArray());
return is;
}
}

注意第三个转换函数Bitmap2InputStream()方法内的第二句

bm.compress(Bitmap.CompressFormat.PNG, quality, baos);

非常明显这里将Bitmap压缩为了PNG格式。也就是说GIF被压缩成了PNG!

这个时候非常容易想到,把PNG改成GIF不就能够了嘛!~

可是点击进入 CompressFormat 类后发现,仅仅支持三种格式

public enum CompressFormat {
JPEG (0),
PNG (1),
WEBP (2); //Sdk-14后開始支持
}

而这三种都不能实现压缩成gif格式的流。

这个时候我就突发奇想了,能不能不转换成 input stream!看看Movie还支持其它的什么转换方法么!

结果是这种:

Movie.decodeByteArray(byte[] data, int offset, int length)

Movie.decodeFile(String pathName)

Movie.decodeStream(InputStream is)

1.decodeByteArray通过 byte[] 转换的话。不管bitmap还是drawable都要经过下一步骤

    /**
* bitmap -> byte[]
*/
public byte[] Bitmap2Bytes(Bitmap bm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}

能够看到还是要压缩。所以这种方法放弃。

2.decodeFile通过文件转化的方法好像行之有效。并且Image自带方法setImageURL(Uri uri),那么重写它看看!

@Override
public void setImageURI(Uri uri) {
mMovie = Movie.decodeFile(uri.toString());
if (mMovie == null) {
super.setImageURI(uri);
}
}

怀着激动的心情执行。

结果 —— 并没有显示不论什么东西。

—(2015.7.21更新。找到方法破解该问题!

可略过以下一大段直接跳到最后看更新内容!)—


希望破灭了~

问度娘要安慰~

检索后。发现大部分情况都是将GIF转换成了PNG/JEPG就不了了之了,去 stackoverflow 发现有人问却也没有人给出行之有效的办法,去Github找各种第三方。发现非常多也仅仅是和我【学习篇】实现一样,并不能解决实际问题(我如今项目确实要用到,从sd卡读取图片并显示,支持gif和其它格式图片)。

搜索了一下午。也有了一些思路,大概例如以下:

方案1.通过先把 gif 图片转换为若干帧的 bitmap 保存起来,然后等要使用的时候,再合成 gif 。并利用线程播放。

(參看:《Android 载入.gif格式图片》

这种方法听上去就比較麻烦,看了代码量又好大我没时 (zi) 间 (xi) 看……)

方案2.自己写一个压缩类,实现压缩 gif 格式图片。

(參看:《java图片压缩处理 支持gif》《最终搞定多张JPG图片转成GIF动画这个难题,解决方法例如以下。》

这种方法听上去简单好用,可是代码量好大我没仔 (shi) 细 (jian) 看……)

方案3.通过第三方写好的拿来用。依据需求更改源代码。

(參看:

p=2#comments">《android开源库android-gif-drawable的使用》“GifView项目源代码”。这两个好像看评论第一个更好,用 JNI 攻克了内存泄露的问题。

可是。我用的是第二个……第二个自带 javadoc。自学没有问题。)

—(2015.8.28更新,还是把我逼到用jni的地步,已经会用。不难。比movie效率高!

往后跳过看。)—

懒人看过来:

我是个聪明人。也就是俗称的懒人~我一定是朝着“怎么能高速解决这个问题”这个方向走的。我如今攻克了,但并非完美地解决的方法,鼓舞大家都是勤快人。能自己去琢磨~也能够參考我的做法。

首先下载“GifView项目源代码”。文件夹结构是这个样子的

能够看到有个Activity!~那么说明能够直接执行,我们看一下。

哇,感觉好强大的样子。

可是翻阅Activity的实现发现,它都是调用项目res资源的gif,这个我们已经实现了,看看它还有没有其它的设置图片方法?

然后我们看看doc,哇好全的样子(自学就靠它了!)

在GifView的API中。我们发现了三个方法:

经试验。setGifImage(String filePath)能够播放本地(指SD卡)gif,可是也仅仅是支持 .gif 格式的,假设路径目标是其它格式(比方.png),程序就会挂掉。

感觉又回到了原点……忙了一天了。毫无成果。非常挫败。

这时候我懒人思想冒了出来:既然是读取文件路径,那就说明能够预先推断后缀名是否是gif。假设是的话就调用该方法,不是调用默认方法不就能够了。

另外,我发现。GifView并没有重写父类的“onDraw()”,”onMeasure()”。“setImageResource(int)”等方法,而我的 MyGifView 刚好写了,于是结合一下。用 MyGifView 继承 GifView!在MyGifView进行改动!

@Override
public void setImageURI(Uri uri) {
String path = uri.toString();
if (isGif(path)) { //依据路径名推断后缀是否为gif
setGifImage(path); //调用父类GifView的方法
} else {
this.pauseGifAnimation(); //暂停之前动画,不然设置别的图片的时候,原来的gif还在播放动画
super.setImageURI(uri); //调用原始父类ImageView的方法
}
}
//以下两个方法能够写到工具类里
/**
* @Description 推断是否是gif图片
* @param path 文件路径
* @return true 是gif; false 不是gif
*/
public boolean isGif(final String path) {
if ("gif".equals(getExtFromFileName)) {
return true;
}
return false;
}
/**
* @Description 获取文件后缀名
* @param fileName 文件名称或文件路径
* @return 后缀名
*/
public String getExtFromFileName(final String fileName) {
int dot = fileName.lastIndexOf('.'); //取得最后一个.的位置
if (dot != -1) {
return fileName.substring(dot + 1, fileName.length());
}
return "";
}

在Activity里面调用

    myGifView.setImageURI(Uri.parse("mnt/sda/sda1/test/coffee.gif"));
//myGifView.setImageURI(Uri.parse("mnt/sda/sda1/test/me.png"));

这个时候执行,发现gif和普通图片都能正常显示。可是卡顿非常严重!

用GifView的设置方法却不会。

原因我也找到了,是由于我的MyGifView重写了setImageDrawable(Drawable drawable)setImageBitmap(Bitmap bitmap),依据打印发现播放的时候,这两个方法频繁被调用,可能GifView播放动画的时候用到了这两个方法,反正我如今也用不到这两个方法,于是。我就干脆屏蔽掉了。

屏蔽掉之后果然不卡顿了。

最后有人要问了,那我的图片不在本地怎么办呀?我从网上下下来的图片怎么办呀?

我的懒人思想:

方案1. 你下下来先保存嘛……

方案2. 假设你能够得到byte[]数据的话,能够试试Movie的处理byte[]的办法,也能够自己把byte[]转换成InputStream。然后调用setMovie(InputStream),还能够试试用GifView处理byte[]的方法。

总而言之,能绕过bitmap转input stream就好办!

原来这篇博客写了八千字了……该结贴了。


源代码地址:https://github.com/Xieyupeng520/MyGifView_V1.1


2015.7.21 更新。解决 Movie.decodeFile 不起作用的问题

本来用上面的第三方攻克了也挺好。可是我发现几个问题,第三方的代码播放帧数较多的 gif 图片卡顿非常严重!并且 gif 放大后清晰度也失真严重。

非常幸运的是。我在网上找解决的方法的时候,找到了个非常easy的方法,还是用Movie。并且用 Movie 通过流的方式播放 gif 的效果是最好的。和原图无差。

这种方法我之前也用过。就是用 Movie 的 decodeFile 方法,以下是 GifView 里面重写父类的 setImageURI 方法。

@Override
public void setImageURI(Uri uri) {
mMovie = Movie.decodeFile(uri.toString());
if (mMovie == null) {
super.setImageURI(uri);
}
}

这个之前试过是不行的。

看我找到的解决的方法。

方法一:

參考了“贴吧五楼”和博客 《android 播放网络或本地gif格式的动态图片》后。得到的解决的方法是:

将FileInputStream转化为btye[]数组。然后调用Movie.decodeByteArray(byte[] array,0,array.length); 去完毕。

写成代码是这个样子:

@Override
public void setImageURI(Uri uri) {
InputStream is = new FileInputStream(uri.toString());
//把 sream 转换成 byte[]
byte[] array = streamToBytes(iStream);
mMovie = Movie.decodeByteArray(array, 0, array.length);
if (mMovie == null) {
super.setImageURI(uri);
} //设置图片宽高
Bitmap bitmap = BitmapFactory.decodeByteArray(array, 0, array.length);
if (bitmap != null) {
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
bitmap.recycle(); //不须要了,释放掉
}
} //把 sream 转换成 byte[]
private byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024); //亲測也可不写1024
byte[] buffer = new byte[1024]; //缓存buffer
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len); //写入输出流
}
} catch (Exception e) {
e.printStackTrace();
}
return os.toByteArray();
}

方法二:

假设你连方法一都嫌麻烦的话,那方法二真的是代码少到死!

直接上代码!

@Override
public void setImageURI(Uri uri) {
//就以下两句是新加的,其它都是原来的
InputStream is = new BufferedInputStream(new FileInputStream(uri.toString()));
is.mark(16 * 1024);
//调用之前封装好的setMovie(InputStream is)方法
setMovie(is);
if (mMovie == null) {
super.setImageURI(uri);
}
}
/**
* 设置movie
* @param iStream 输入流
*/
public void setMovie(InputStream iStream) {
mMovie = Movie.decodeStream(iStream);
if (mMovie == null) { //说明不是gif。直接退出
return;
}
//设置图片宽高
Bitmap bitmap = BitmapFactory.decodeStream(iStream);
if (bitmap == null) {
return;
}
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
bitmap.recycle();
}

只是有个问题是,Bitmap bitmap = BitmapFactory.decodeStream(iStream);这一句得到的bitmap为null,用方法一不会出现此问题。

參考:《解决Android中Movie导入播放GIF图片文件异常IOException.reset》

我也真是运气,点进去一个“不相关”的问题,都能找到解决的方法。

所以提醒大家找问题的时候,不要局限于你自己的那几个关键字哦!

~


2015.8.28 更新!使用强大的android-gif-drawable开源库,效率比Movie还高!

命运多舛,又出现新问题了,7.21更新的方法在4.0。4.2,5.0的机器上測试都没问题,却发如今Android 4.4出现调用movie.draw(canvas,0,0)崩溃的情况。我也崩溃了。

于是后来我用了第三方调用jni的android-gif-drawable开源库。之前不用它就是怕麻烦,用过之后发现不像想象中那样复杂。如今记录一下使用过程。

首先打开

p=2#comments">《android开源库android-gif-drawable的使用》,过一下前面8点,讲了怎么样把 jni 拷到自己项目中。

后面的就是一些 API 了。

简单使用,先新建一个GifDrawable,然后把GifDrawable设置到 GifImageView / GifImageButton / GifTextView 中。就OK了!

    GifImageView gif = (GifImageView) findViewById(R.id.hisGifView);

    GifDrawable gifFromResource = new GifDrawable( getResources(), R.drawable.run );
gif.setImageDrawable(gifFromResource);

值得一提的是,文章其中用的是1.0.8版本号,如今尽管已经更新到了1.1.9版本号了。可是我用的仍然是1.0.8的,由于1.1.9的so直接导入执行报错,而我还不知道怎样解决。

1.0.8版本号缺点是不能在Android 5.0 + 的手机上正常执行。


8.28更新-Demo源代码下载:https://github.com/Xieyupeng520/MyGifView_V1.3



假设你有不论什么问题。欢迎留言告诉我!

【Android实战】记录自学自己定义GifView过程,能同一时候支持gif和其它图片!【有用篇】的更多相关文章

  1. 【Android自己定义View实战】之自己定义超简单SearchView搜索框

    [Android自己定义View实战]之自己定义超简单SearchView搜索框 这篇文章是对之前文章的翻新,至于为什么我要又一次改动这篇文章?原因例如以下 1.有人举报我抄袭,原文链接:http:/ ...

  2. Android实战之 万能的接口回调

    转载请标明原地址:http://blog.csdn.net/gaolei1201/article/details/47084111 前言:本人也算是自学"成才",呵呵,大学时尽管学 ...

  3. 苹果电脑基本设置+Linux 命令+Android 实战集锦

    本文微信公众号「AndroidTraveler」首发. 背景 大多数应届毕业生在大学期间使用的比较多的是 windows 电脑,因此初入职场如果拿到一台苹果电脑,可能一时间不能够很快的上手.基于此,这 ...

  4. sikuli实战记录

    最近,为了解放运营人力,需要对某国企的系统进行自动化操作.该系统使用的是https,需要加载证书,而且非得用IE才行,firefox无法正常的加载证书.而selenium启动的IE是纯净的,不会加载任 ...

  5. Android内核驱动程序的编写和编译过程

    注意:涉及的代码为android内核代码而不是android源码. 在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非ip ...

  6. celery+RabbitMQ 实战记录2—工程化使用

    上篇文章中,已经介绍了celery和RabbitMQ的安装以及基本用法. 本文将从工程的角度介绍如何使用celery. 1.配置和启动RabbitMQ 请参考celery+RabbitMQ实战记录. ...

  7. Android系统的镜像文件的打包过程

    在前面一篇文章中,我们分析了Android模块的编译过程.当Android系统的所有模块都编译好之后,我们就可以对编译出来的模块文件进行打包了.打包结果是获得一系列的镜像文件,例如system.img ...

  8. 一步步Cobol 400上手自学入门教程04 - 过程部

    过程部的作用:编写程序要执行的语句,是程序的核心. 结构: 基本语句 INITIALIZE 设置数据项的初始值 ACCEPT 接收从键盘或指定设备上获得输入数据. 例子: 当批处理文件读到调用ACCP ...

  9. 升级 Elasticsearch 集群数量实战记录

    搜索引擎 升级 Elasticsearch 集群数量实战记录 现在线上有一个elasticsearch集群搜索服务有三台elasticsearch实例(es1.es2.es3),打算将其升级为5台(增 ...

随机推荐

  1. P2668 斗地主 贪心+深搜

    题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4< ...

  2. Java&Xml教程(八)使用JDOM将Java对象转换为XML

    在前面的教程中我们学习了如何使用JDOM解析和修改XML文件内容,本节介绍如何将Java对象转换为XML数据并生成文件. JDOM的Document类提供了便捷的方法创建元素和属性,XMLOutput ...

  3. 如何利用sql注入进行爆库

    SQL注入能做什么 在<SQL注入基础>一文介绍了SQL注入的基本原理和实验方法,那接下来就要问一下,SQL注入到底能什么? 估计很多朋友会这样认为:利用SQL注入最多只能获取当前表中的所 ...

  4. PHP7 上传文件报错 Internal Server Error 解决方法

    打开Apache配置httpd.conf.在最后添加FcgidMaxRequestLen指令一个足够大的值(以字节为单位),例如 FcgidMaxRequestLen 100000000 最后重新启动 ...

  5. JS的filter用法

    filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素. 和map()类似,Array的filter()也接收一个函数.和map()不同的是,filter()把传入的函 ...

  6. Oracle RAC 后台进程

    LMS  - Gobal         全局缓存服务进程 LMD  - Global Enqueue Service Daemon 全局查询服务守护进程 LMON -  全局服务器监控进程 LCK0 ...

  7. 【计算几何】二维凸包——Graham's Scan法

    凸包 点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内.右图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包. 一组平面上的点, ...

  8. acm学习指引

    acm学习心得及书籍推荐   一般要做到50行以内的程序不用调试.100行以内的二分钟内调试成功.acm主要是考算法的,主要时间是花在思考算法上,不是花在写程序与debug上. 下面给个计划练练: 第 ...

  9. Luogu P2847 [USACO20DEC]Moocast(gold)奶牛广播-金

    解题思路 要保证图是强连通的,用因为给出的边全部都是双向边.考虑树形的结构,在一棵树上的$N$个节点一定是强连通的.他们都能够互相到达.又要保证树上的$n-1$条边中的最长的一条边最小.那就用Krus ...

  10. 个人 NABCD

    失物招领APP个人NABCD Need: 在朋友圈中,QQZone中我们长仓可以看到失物招领这方面的信息,又没有学校中专门使用的失物招领平台,所以根据这个信息,决定开发一款解决这方面问题的APP来满足 ...